Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: CI workflow

on: pull_request

jobs:
ci:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Install
run: npm ci
- name: test
run: npm test

1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ test-report.*
junit.xml
*.log
*.orig
package-lock.json
yarn.lock
.awcache
public
Expand Down
1 change: 1 addition & 0 deletions config/test/jest.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ module.exports = {
restoreMocks: true,
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['<rootDir>/config/test/setup-after.ts'],
moduleDirectories: ['<rootDir>/src', 'node_modules'],
};
13,100 changes: 13,100 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

11 changes: 6 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@
"lodash.flowright": "^3.5.0",
"lodash.get": "^4.4.2",
"lodash.merge": "^4.6.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-promise-tracker": "^2.1.0",
"react-router-dom": "^5.2.0",
"react-spinners": "^0.11.0",
Expand All @@ -57,16 +57,17 @@
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^12.0.0",
"@testing-library/react-hooks": "^7.0.1",
"@testing-library/user-event": "^14.4.3",
"@types/jest": "^26.0.24",
"@types/react": "^17.0.15",
"@types/react-dom": "^17.0.9",
"@types/react": "^18.0.24",
"@types/react-dom": "^18.0.8",
"@types/react-router-dom": "^5.1.8",
"babel-loader": "^8.2.2",
"dotenv-webpack": "^7.0.3",
"html-webpack-plugin": "^5.3.2",
"jest": "^27.0.6",
"npm-run-all": "^4.1.5",
"react-test-renderer": "^17.0.2",
"react-test-renderer": "^18.2.0",
"rimraf": "^3.0.2",
"ts-jest": "^27.0.4",
"typescript": "^4.3.5",
Expand Down
10 changes: 10 additions & 0 deletions src/@types/fixes.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Fixes TS2694
declare global {
namespace React {
/** Fixes React 18 compatibility issues with formik: https://github.com/jaredpalmer/formik/issues/3546#issuecomment-1127014775 */
type StatelessComponent<P> = React.FunctionComponent<P>;
}
}

// Fixes TS2669
export {};
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@

import React from 'react';
import { render, screen, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

import { ConfirmationDialogComponent } from './confirmation-dialog.component';


describe('Confirmation dialog Component specs', () => {

let props: any;

beforeEach(() => {
props = {isOpen: true,
onAccept: jest.fn(),
onClose: jest.fn().mockImplementation(() => {}),
title: 'Title',
labels: {closeButton: 'Cancel', acceptButton:'Accept'}};
});

it('Should display a dialog box when isOpen is true', () => {
// Arrange

// Act
render(<ConfirmationDialogComponent {...props} />);

const dialog = screen.getByRole('dialog');

// Assert
expect(dialog).toBeInTheDocument();
});

it('Should not display a dialog box when isOpen is false', () => {
// Arrange
props.isOpen = false;

// Act
render(<ConfirmationDialogComponent {...props} />);

const dialog = screen.queryByRole('dialog');

// Assert
expect(dialog).not.toBeInTheDocument();
});

it('Should display cancel and accept buttons with proper text', async () => {
// Arrange

// Act
render(<ConfirmationDialogComponent {...props} />);

const dialog = screen.getByRole('dialog');

const [cancelButton, acceptButton] = await within(dialog).findAllByRole('button');


// Assert
expect(cancelButton.textContent).toEqual("Cancel");
expect(acceptButton.textContent).toEqual("Accept");
});

it('Should call cancel method when cancel button is pushed', async () => {
// Arrange


// Act
render(<ConfirmationDialogComponent {...props} />);

const dialog = screen.getByRole('dialog');

const [cancelButton, acceptButton] = await within(dialog).findAllByRole('button');

await userEvent.click(cancelButton);

// Assert
expect(props.onClose).toHaveBeenCalled();

});
});
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ interface Props {
onClose: () => void;
title: string | React.ReactNode;
labels: LabelProps;
children: React.ReactNode;
}

export const ConfirmationDialogComponent: React.FunctionComponent<Props> = props => {
const { isOpen, onAccept, onClose, title, labels, children } = props;
const { isOpen, onAccept, onClose, title, labels, children} = props;

const handleAccept = () => {
onAccept();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { renderHook, act } from '@testing-library/react-hooks';

import { useConfirmationDialog} from './confirmation-dialog.hook';
import { createEmptyLookup, Lookup } from './../../../common/models/lookup';

describe('use Confirmation Dialog specs', () => {
it('Default case. Should return isOpen as false,itemToDelete with its default value.', () => {
//Arrange

//Act
const {result} = renderHook(() => useConfirmationDialog());

//Assert
expect(result.current.isOpen).toEqual(false);
expect(result.current.itemToDelete).toEqual({id: '', name: ''});
});

it('When dialog opens the states isOpen and itemToDelete should be updated.', () => {
//Arrange
const targetItem: Lookup = { id: '123', name: 'John Doe'};

//Act
const {result} = renderHook(() => useConfirmationDialog());
act(() => result.current.onOpenDialog(targetItem));

//Assert
expect(result.current.isOpen).toEqual(true);
expect(result.current.itemToDelete).toEqual(targetItem);
});

it('When item to delete is accepted the state itemToDelete should be updated to default value.', () => {
//Arrange
const targetItem: Lookup = { id: '123', name: 'John Doe'};

//Act
const {result} = renderHook(() => useConfirmationDialog());
act(() => result.current.onAccept());

//Assert
expect(result.current.itemToDelete).toEqual({id:'', name:''});
});

it('When dialog is closed the state isOpen should be false.', () => {
//Arrange

//Act
const {result} = renderHook(() => useConfirmationDialog());
act(() => result.current.onClose());

//Assert
expect(result.current.isOpen).toEqual(false);
});
});
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import React from 'react';
import { createEmptyLookup, Lookup } from 'common/models';
import { createEmptyLookup, Lookup } from './../../../common/models/lookup';

export const useConfirmationDialog = () => {
const [isOpen, setIsOpen] = React.useState(false);
const [itemToDelete, setItemToDelete] = React.useState(createEmptyLookup());
const [itemToDelete, setItemToDelete] =
React.useState(createEmptyLookup());

return {
isOpen,
Expand Down
2 changes: 1 addition & 1 deletion src/core/router/router.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {

export const RouterComponent: React.FunctionComponent = () => {
return (
<HashRouter>
<HashRouter>
<Switch>
<Route
exact={true}
Expand Down
68 changes: 68 additions & 0 deletions src/pods/project/project.mapper.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import * as mapper from './../../common/mappers/collection.mapper';
import * as apiModel from './api/project.api-model';
//import * as viewModel from './project.vm';
import { mapProjectFromApiToVm } from './project.mapper';


describe('Project Mapper specs', () => {
it('When api project is undefined should return an empty project', () => {
// Arrange
const projectApi: apiModel.Project = undefined;

// Act
const result = mapProjectFromApiToVm(projectApi);

// Assert
expect(result).toEqual({id: '', name: '', externalId: '', comments: '', isActive: false, employees: []});
});

it('When api project is null should return an empty project', () => {
// Arrange
const projectApi: apiModel.Project = null;

// Act
const result = mapProjectFromApiToVm(projectApi);

// Assert
expect(result).toEqual({id: '', name: '', externalId: '', comments: '', isActive: false, employees: []});
});

it('When api project is something should return the proper view model project', () => {
// Arrange
const project: apiModel.Project = {
id: '111',
name: 'Test Project',
externalId: '123',
comments: 'Nothing to comment...',
isActive: true,
employees: [{id: '999', isAssigned: true, employeeName: 'John Doe'}]
};

// Act
const result = mapProjectFromApiToVm(project);

// Assert
expect(result).toStrictEqual({id: '111', name: 'Test Project', externalId: '123', comments: 'Nothing to comment...', isActive: true,
employees: [{id: '999', isAssigned: true, employeeName: 'John Doe'}]});
});
it('Should call mapToCollection', () => {
// Arrange
const project: apiModel.Project = {
id: '111',
name: 'Test Project',
externalId: '123',
comments: 'Nothing to comment...',
isActive: true,
employees: [{id: '999', isAssigned: true, employeeName: 'John Doe'}]};

const stub = jest.spyOn(mapper, 'mapToCollection');


// Act
const result = mapProjectFromApiToVm(project);

// Assert
expect(stub).toHaveBeenCalled();
expect(stub).toHaveBeenCalledWith([{id: '999', isAssigned: true, employeeName: 'John Doe'}], expect.any(Function));
});
});
2 changes: 1 addition & 1 deletion src/pods/project/project.mapper.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { mapToCollection } from 'common/mappers';
import { mapToCollection } from './../../common/mappers/collection.mapper';
import * as apiModel from './api/project.api-model';
import * as viewModel from './project.vm';

Expand Down