Skip to content

Commit 48abf11

Browse files
author
Philipp Duwe
committed
feat 1398: Introduction of UI tests into the Configuration UI project
1 parent ae14c5e commit 48abf11

File tree

8 files changed

+6000
-6235
lines changed

8 files changed

+6000
-6235
lines changed

components/inspectit-ocelot-configurationserver-ui/.eslintrc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
"env": {
33
"browser": true,
44
"es6": true,
5-
"node": true
5+
"node": true,
6+
"jest": true
67
},
78
"plugins": [
89
"import",
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
module.exports = {
2+
collectCoverageFrom: ['**/*.{js,jsx,ts,tsx}', '!**/*.d.ts', '!**/node_modules/**'],
3+
moduleNameMapper: {
4+
// Handle CSS imports (with CSS modules)
5+
// https://jestjs.io/docs/webpack#mocking-css-modules
6+
'^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy',
7+
8+
// Handle CSS imports (without CSS modules)
9+
'^.+\\.(css|sass|scss)$': '<rootDir>/__mocks__/styleMock.js',
10+
11+
// Handle image imports
12+
// https://jestjs.io/docs/webpack#handling-static-assets
13+
'^.+\\.(png|jpg|jpeg|gif|webp|avif|ico|bmp|svg)$/i': `<rootDir>/__mocks__/fileMock.js`,
14+
15+
// Handle module aliases
16+
'^@/components/(.*)$': '<rootDir>/components/$1',
17+
},
18+
// Add more setup options before each test is run
19+
// setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
20+
testPathIgnorePatterns: ['<rootDir>/node_modules/', '<rootDir>/.next/'],
21+
testEnvironment: 'jsdom',
22+
transform: {
23+
// Use babel-jest to transpile tests with the next/babel preset
24+
// https://jestjs.io/docs/configuration#transform-objectstring-pathtotransformer--pathtotransformer-object
25+
'^.+\\.(js|jsx|ts|tsx)$': ['babel-jest', { presets: ['next/babel'] }],
26+
},
27+
transformIgnorePatterns: ['/node_modules/', '^.+\\.module\\.(css|sass|scss)$'],
28+
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
29+
};
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import '@testing-library/jest-dom/extend-expect';
2+
import { setConfig } from 'next/config';
3+
import config from './next.config';
4+
setConfig(config);
Lines changed: 32 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,41 @@
1-
const withCSS = require('@zeit/next-css')
1+
const withCSS = require('@zeit/next-css');
22
const isProduction = process.env.NODE_ENV === 'production';
33

44
module.exports = withCSS({
5-
distDir: '../.next',
5+
distDir: '../.next',
66

7-
// Each page will be exported as a directory
8-
exportTrailingSlash: true,
7+
// Each page will be exported as a directory
8+
exportTrailingSlash: true,
99

10-
assetPrefix: isProduction ? '/ui' : '',
10+
assetPrefix: isProduction ? '/ui' : '',
1111

12-
// Will only be available on the server side
13-
serverRuntimeConfig: {
14-
},
12+
// Will only be available on the server side
13+
serverRuntimeConfig: {},
1514

16-
// Will be available on both server and client
17-
publicRuntimeConfig: {
18-
// used in '/components/basics/Link.js', for more details go to the component itself
19-
linkPrefix: isProduction ? '/ui' : ''
20-
},
15+
// Will be available on both server and client
16+
publicRuntimeConfig: {
17+
// used in '/components/basics/Link.js', for more details go to the component itself
18+
linkPrefix: isProduction ? '/ui' : '',
19+
},
2120

22-
// Required for successfully importing CSS files (e.g. from PrimeReact)
23-
// See: https://github.com/zeit/next-plugins/issues/273#issuecomment-430597241
24-
webpack: function (config) {
25-
config.module.rules.push({
26-
test: /\.(eot|woff|woff2|ttf|svg|png|jpg|gif)$/,
27-
use: {
28-
loader: 'url-loader',
29-
options: {
30-
limit: 100000,
31-
name: '[name].[ext]'
32-
}
33-
}
34-
})
35-
return config
36-
},
21+
// Required for successfully importing CSS files (e.g. from PrimeReact)
22+
// See: https://github.com/zeit/next-plugins/issues/273#issuecomment-430597241
23+
webpack: function (config) {
24+
config.module.rules.push({
25+
test: /\.(eot|woff|woff2|ttf|svg|png|jpg|gif)$/,
26+
use: {
27+
loader: 'url-loader',
28+
options: {
29+
limit: 100000,
30+
name: '[name].[ext]',
31+
},
32+
},
33+
});
34+
return config;
35+
},
3736

38-
env: {
39-
VERSION: process.env.CIRCLE_TAG || "SNAPSHOT",
40-
BUILD_DATE: new Date().toUTCString()
41-
}
42-
})
37+
env: {
38+
VERSION: process.env.CIRCLE_TAG || 'SNAPSHOT',
39+
BUILD_DATE: new Date().toUTCString(),
40+
},
41+
});

components/inspectit-ocelot-configurationserver-ui/package.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,23 @@
2222
"format": "prettier --check \"./src/**/*.+(js|jsx|json|css|md)\"",
2323
"format:write": "prettier --write \"./src/**/*.+(js|jsx|json|css|md)\"",
2424
"storybook": "start-storybook -p 6006",
25-
"build-storybook": "build-storybook"
25+
"build-storybook": "build-storybook",
26+
"test": "jest",
27+
"test:watch": "jest --watch"
2628
},
2729
"dependencies": {
30+
"@reduxjs/toolkit": "^1.8.2",
31+
"@testing-library/jest-dom": "^5.16.4",
32+
"@testing-library/react": "^11.2.5",
33+
"@testing-library/user-event": "^14.2.0",
2834
"@zeit/next-css": "^1.0.1",
2935
"ace-builds": "^1.4.5",
3036
"axios": "^0.26.1",
37+
"babel-jest": "^28.1.0",
3138
"classnames": "^2.2.6",
3239
"dateformat": "^3.0.3",
40+
"jest": "^28.1.0",
41+
"jest-environment-jsdom": "^28.1.0",
3342
"js-yaml": "^3.13.1",
3443
"jwt-decode": "^2.2.0",
3544
"lodash": "^4.17.21",
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { render, screen, within } from '@testing-library/react';
2+
import LoginView from '../LoginView';
3+
import '@testing-library/jest-dom';
4+
import React from 'react';
5+
import { storeWrapper } from '../../../lib/reduxTestUtils';
6+
import { authentication } from '../../../redux/ducks';
7+
import userEvent from '@testing-library/user-event';
8+
9+
const setup = () => {
10+
const reducers = { authentication };
11+
return render(storeWrapper(<LoginView />, reducers));
12+
};
13+
14+
describe('LoginView', () => {
15+
//Arrange
16+
beforeEach(() => setup());
17+
18+
it('renders successfully', () => {
19+
//Arrange
20+
const logo = screen.getByRole('img');
21+
const heading1 = screen.getByText('inspectIT Ocelot');
22+
const heading2 = screen.getByText('Configuration Server');
23+
const username = screen.getByRole('textbox', { placeholder: 'Username' });
24+
const password = screen.getByRole('textbox', { placeholder: 'Password' });
25+
const loginButton = screen.getByRole('button', { name: 'Login' });
26+
const footer = screen.getByText(/inspectit ocelot configuration server/i);
27+
const docsLink = within(footer).getByRole('link', { name: 'Docs' });
28+
const githubLink = within(footer).getByRole('link', { name: 'Github' });
29+
30+
//Act - not required
31+
32+
//Assert
33+
expect(logo).toBeInTheDocument();
34+
expect(heading1).toBeInTheDocument();
35+
expect(heading2).toBeInTheDocument();
36+
expect(username).toBeInTheDocument();
37+
expect(password).toBeInTheDocument();
38+
expect(loginButton).toBeInTheDocument();
39+
expect(docsLink).toBeInTheDocument();
40+
expect(githubLink).toBeInTheDocument();
41+
});
42+
43+
it('disables the login button when username is missing', async () => {
44+
//Arrange
45+
const loginButton = screen.getByRole('button', { name: 'Login' });
46+
const password = screen.getByPlaceholderText('Password');
47+
48+
//Act
49+
await userEvent.type(password, 'password123');
50+
51+
//Assert
52+
expect(loginButton).toBeDisabled();
53+
});
54+
55+
it('disables the login button when password is missing', async () => {
56+
//Arrange
57+
const loginButton = screen.getByRole('button', { name: 'Login' });
58+
const username = screen.getByPlaceholderText('Username');
59+
60+
//Act
61+
await userEvent.type(username, 'userName');
62+
63+
//Assert
64+
expect(loginButton).toBeDisabled();
65+
});
66+
67+
it('enables the login button when username and password are present', async () => {
68+
//Arrange
69+
const loginButton = screen.getByRole('button', { name: 'Login' });
70+
const username = screen.getByPlaceholderText('Username');
71+
const password = screen.getByPlaceholderText('Password');
72+
73+
//Act
74+
await userEvent.type(username, 'userName');
75+
await userEvent.type(password, 'password123');
76+
77+
//Assert
78+
expect(loginButton).toBeEnabled();
79+
});
80+
});
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { Provider } from 'react-redux';
2+
import React from 'react';
3+
import { configureStore } from '@reduxjs/toolkit';
4+
5+
/**
6+
* wrapper function for testing purposes that include a redux store
7+
*/
8+
export function storeWrapper(jsx, reducers) {
9+
const store = mockStore(reducers);
10+
return <Provider store={store}>{jsx}</Provider>;
11+
}
12+
13+
/**
14+
* store configuration function that supplies a store with the provided reducers
15+
*/
16+
export function mockStore(reducers) {
17+
return configureStore({
18+
reducer: reducers,
19+
middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: false }),
20+
});
21+
}

0 commit comments

Comments
 (0)