Skip to content

Commit 2854e7e

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

File tree

16 files changed

+6210
-6286
lines changed

16 files changed

+6210
-6286
lines changed

.github/workflows/configuration_ui_test.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,8 @@ jobs:
3333

3434
- name: Run Prettier
3535
working-directory: ${{env.working-directory}}
36-
run: yarn format
36+
run: yarn format
37+
38+
- name: Run Jest
39+
working-directory: ${{env.working-directory}}
40+
run: yarn test

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: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# HowTo Front End Testing
2+
3+
This section gives a short introduction of how to write front end tests for Configuration Server UI and what to cover.
4+
5+
## Introduction:
6+
These testing practices were thought of with regard to the current architecture and project structure. Also, this can
7+
and will still grow and change over time, this is the first step into introducing front end tests. We distinguish
8+
between render and functionality tests. Render tests will make use of snapshots to check whether the DOM changed
9+
unknowingly.
10+
Functionality tests are covering the functionality of a component. The focus here is to
11+
imitate user (inter-)actions and assert the expected state.
12+
13+
## Render Tests
14+
A render test takes a component, checks its DOM (Document Object Model) and compares it to a snapshot file that is stored from an
15+
earlier point in time. When changes are detected, the test will fail and thereby indicate that potentially
16+
unwanted changes happened.
17+
18+
It is recommended to test, at the very least, smaller components via snapshots. As already mentioned, snapshot tests will
19+
fail when a change in the DOM is found, so testing big components will result in them failing often
20+
(whenever a change in any subcomponent happens) and will also result in very big and unreadable snapshot files. Bigger components
21+
will have a shallow render test that only render a component one level deep and disregarding child components' contents.
22+
23+
## Functionality Tests:
24+
It makes sense to write tests that verify the functionalities of every component implemented. With a functionality test,
25+
we check for the (basic) functionality of a component. This can be a disabled button or an error message of a validation
26+
when a certain text-field is empty.
27+
28+
This type of test usually covers the user interactions and verifies their functionality. Checking for the presence of
29+
certain elements is not covered here. We do not distinguish whether the component is very small or has many children, but
30+
it is highly advised to isolate the tests for every scenario of interaction and not test many scenarios in one test.
31+
At a later point in time, E2E Tests might be introduced as well. Those should be treated with caution however, since they
32+
are very expensive compared to basic component (functionality) tests.
33+
34+
## Helpful links:
35+
36+
https://testing-library.com/docs/
37+
38+
https://kentcdodds.com/

components/inspectit-ocelot-configurationserver-ui/README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,24 @@ yarn dev
2020

2121
The development server can be reached at [http://localhost:3000](http://localhost:3000).
2222

23+
#### Testing
24+
25+
Using the following command, the front end tests can be run. This will execute 'jest'.
26+
27+
```bash
28+
yarn test
29+
```
30+
31+
or
32+
33+
```bash
34+
yarn test:watch
35+
```
36+
37+
to run the tests in watch mode.
38+
39+
More information about how to test in [FRONTENDTESTS.md](FRONTENDTESTS.md)
40+
2341
#### Storybook
2442

2543
The project also contains [Storybook](https://storybook.js.org/) which supports the development of components by providing an isolated sandbox UI for testing these components.
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: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { render, screen } 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+
let container;
16+
//Arrange
17+
beforeEach(() => {
18+
const renderedDom = setup();
19+
container = renderedDom.container;
20+
});
21+
22+
it('renders successfully', () => {
23+
expect(container).toMatchSnapshot();
24+
});
25+
26+
it('disables the login button when username is missing', async () => {
27+
//Arrange
28+
const loginButton = screen.getByRole('button', { name: 'Login' });
29+
const password = screen.getByPlaceholderText('Password');
30+
31+
//Act
32+
await userEvent.type(password, 'password123');
33+
34+
//Assert
35+
expect(loginButton).toBeDisabled();
36+
});
37+
38+
it('disables the login button when password is missing', async () => {
39+
//Arrange
40+
const loginButton = screen.getByRole('button', { name: 'Login' });
41+
const username = screen.getByPlaceholderText('Username');
42+
43+
//Act
44+
await userEvent.type(username, 'userName');
45+
46+
//Assert
47+
expect(loginButton).toBeDisabled();
48+
});
49+
50+
it('enables the login button when username and password are present', async () => {
51+
//Arrange
52+
const loginButton = screen.getByRole('button', { name: 'Login' });
53+
const username = screen.getByPlaceholderText('Username');
54+
const password = screen.getByPlaceholderText('Password');
55+
56+
//Act
57+
await userEvent.type(username, 'userName');
58+
await userEvent.type(password, 'password123');
59+
60+
//Assert
61+
expect(loginButton).toBeEnabled();
62+
});
63+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`LoginView renders successfully 1`] = `
4+
<div>
5+
<div
6+
class="jsx-3064013252 this"
7+
>
8+
<div
9+
class="jsx-128945688 this"
10+
>
11+
<div
12+
class="jsx-3380344897 this"
13+
>
14+
<img
15+
class="jsx-3380344897 ocelot-head"
16+
src="/static/images/inspectit-ocelot.svg"
17+
/>
18+
<div
19+
class="jsx-3380344897 text-ocelot"
20+
>
21+
inspectIT Ocelot
22+
</div>
23+
<div
24+
class="jsx-3380344897 text-server"
25+
>
26+
Configuration Server
27+
</div>
28+
</div>
29+
<div
30+
class="jsx-128945688 p-inputgroup input"
31+
>
32+
<span
33+
class="jsx-128945688 p-inputgroup-addon"
34+
>
35+
<i
36+
class="jsx-128945688 pi pi-user"
37+
/>
38+
</span>
39+
<input
40+
class="p-inputtext p-component"
41+
placeholder="Username"
42+
style="width: 100%;"
43+
value=""
44+
/>
45+
</div>
46+
<div
47+
class="jsx-128945688 p-inputgroup input"
48+
>
49+
<span
50+
class="jsx-128945688 p-inputgroup-addon"
51+
>
52+
<i
53+
class="jsx-128945688 pi pi-lock"
54+
/>
55+
</span>
56+
<input
57+
class="p-inputtext p-component"
58+
placeholder="Password"
59+
style="width: 100%;"
60+
type="password"
61+
value=""
62+
/>
63+
</div>
64+
<div
65+
class="jsx-128945688 input"
66+
>
67+
<button
68+
class="p-button p-component p-button-text-only p-disabled"
69+
disabled=""
70+
style="width: 100%;"
71+
>
72+
<span
73+
class="p-button-text p-c"
74+
>
75+
Login
76+
</span>
77+
</button>
78+
</div>
79+
</div>
80+
<div
81+
class="jsx-3064013252 build-information"
82+
>
83+
<span
84+
class="jsx-3064013252"
85+
>
86+
inspectIT Ocelot Configuration Server v
87+
(Build Date:
88+
) |
89+
90+
<a
91+
class="jsx-3064013252"
92+
href="http://docs.inspectit.rocks/"
93+
target="_blank"
94+
>
95+
Docs
96+
</a>
97+
98+
|
99+
100+
<a
101+
class="jsx-3064013252"
102+
href="https://github.com/inspectIT/inspectit-ocelot"
103+
target="_blank"
104+
>
105+
Github
106+
</a>
107+
</span>
108+
</div>
109+
</div>
110+
</div>
111+
`;

0 commit comments

Comments
 (0)