Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat 1398: Introduction of UI tests into the Configuration UI project #1458

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
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
10 changes: 7 additions & 3 deletions .github/workflows/configuration_ui_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ jobs:
- uses: actions/checkout@v2

- name: Use Node.js
uses: actions/setup-node@v1
uses: actions/setup-node@v2
with:
node-version: '12.x'
node-version: '16.x'

- name: Install dependencies
working-directory: ${{env.working-directory}}
Expand All @@ -33,4 +33,8 @@ jobs:

- name: Run Prettier
working-directory: ${{env.working-directory}}
run: yarn format
run: yarn format

- name: Run Jest
working-directory: ${{env.working-directory}}
run: yarn test
3 changes: 2 additions & 1 deletion components/inspectit-ocelot-configurationserver-ui/.eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
"env": {
"browser": true,
"es6": true,
"node": true
"node": true,
"jest": true
},
"plugins": [
"import",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules
.next
.next
.yarn/*
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# HowTo Front End Testing

This section gives a short introduction of how to write front end tests for Configuration Server UI and what to cover.

## Introduction:
These testing practices were thought of with regard to the current architecture and project structure. Also, this can
and will still grow and change over time, this is the first step into introducing front end tests. We distinguish
between render and functionality tests. Render tests will make use of snapshots to check whether the DOM changed
unknowingly.
Functionality tests are covering the functionality of a component. The focus here is to
imitate user (inter-)actions and assert the expected state.

## Render Tests
A render test takes a component, checks its DOM (Document Object Model) and compares it to a snapshot file that is stored from an
earlier point in time. When changes are detected, the test will fail and thereby indicate that potentially
unwanted changes happened.

It is recommended to test, at the very least, smaller components via snapshots. As already mentioned, snapshot tests will
fail when a change in the DOM is found, so testing big components will result in them failing often
(whenever a change in any subcomponent happens) and will also result in very big and unreadable snapshot files. Bigger components
will have a shallow render test that only render a component one level deep and disregarding child components' contents.

## Functionality Tests:
It makes sense to write tests that verify the functionalities of every component implemented. With a functionality test,
we check for the (basic) functionality of a component. This can be a disabled button or an error message of a validation
when a certain text-field is empty.

This type of test usually covers the user interactions and verifies their functionality. Checking for the presence of
certain elements is not covered here. We do not distinguish whether the component is very small or has many children, but
it is highly advised to isolate the tests for every scenario of interaction and not test many scenarios in one test.
At a later point in time, E2E Tests might be introduced as well. Those should be treated with caution however, since they
are very expensive compared to basic component (functionality) tests.

## Helpful links:

https://testing-library.com/docs/

https://kentcdodds.com/
18 changes: 18 additions & 0 deletions components/inspectit-ocelot-configurationserver-ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,24 @@ yarn dev

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

#### Testing

Using the following command, the front end tests can be run. This will execute 'jest'.

```bash
yarn test
```

or

```bash
yarn test:watch
```

to run the tests in watch mode.

More information about how to test in [FRONTENDTESTS.md](FRONTENDTESTS.md)

### Linting

Depending on your IDE, linting errors based on the settings in .eslintrc can be shown right in your editor.
Expand Down
29 changes: 29 additions & 0 deletions components/inspectit-ocelot-configurationserver-ui/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
module.exports = {
collectCoverageFrom: ['**/*.{js,jsx,ts,tsx}', '!**/*.d.ts', '!**/node_modules/**'],
moduleNameMapper: {
// Handle CSS imports (with CSS modules)
// https://jestjs.io/docs/webpack#mocking-css-modules
'^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy',

// Handle CSS imports (without CSS modules)
'^.+\\.(css|sass|scss)$': '<rootDir>/__mocks__/styleMock.js',

// Handle image imports
// https://jestjs.io/docs/webpack#handling-static-assets
'^.+\\.(png|jpg|jpeg|gif|webp|avif|ico|bmp|svg)$/i': `<rootDir>/__mocks__/fileMock.js`,

// Handle module aliases
'^@/components/(.*)$': '<rootDir>/components/$1',
},
// Add more setup options before each test is run
// setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
testPathIgnorePatterns: ['<rootDir>/node_modules/', '<rootDir>/.next/'],
testEnvironment: 'jsdom',
transform: {
// Use babel-jest to transpile tests with the next/babel preset
// https://jestjs.io/docs/configuration#transform-objectstring-pathtotransformer--pathtotransformer-object
'^.+\\.(js|jsx|ts|tsx)$': ['babel-jest', { presets: ['next/babel'] }],
},
transformIgnorePatterns: ['/node_modules/', '^.+\\.module\\.(css|sass|scss)$'],
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import '@testing-library/jest-dom/extend-expect';
import { setConfig } from 'next/config';
import config from './next.config';
setConfig(config);
51 changes: 25 additions & 26 deletions components/inspectit-ocelot-configurationserver-ui/next.config.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,38 @@
const withCSS = require('@zeit/next-css')
const withCSS = require('@zeit/next-css');
const isProduction = process.env.NODE_ENV === 'production';

module.exports = withCSS({
distDir: '../.next',
distDir: '../.next',

// Each page will be exported as a directory
trailingSlash: true,

assetPrefix: isProduction ? '/ui' : '',
assetPrefix: isProduction ? '/ui' : '',

// Will only be available on the server side
serverRuntimeConfig: {
},
// Will only be available on the server side
serverRuntimeConfig: {},

// Will be available on both server and client
publicRuntimeConfig: {
// used in '/components/basics/Link.js', for more details go to the component itself
linkPrefix: isProduction ? '/ui' : ''
},
// Will be available on both server and client
publicRuntimeConfig: {
// used in '/components/basics/Link.js', for more details go to the component itself
linkPrefix: isProduction ? '/ui' : '',
},

// Required for successfully importing CSS files (e.g. from PrimeReact)
// See: https://github.com/zeit/next-plugins/issues/273#issuecomment-430597241
webpack: function (config) {
config.module.rules.push({
test: /\.(eot|woff|woff2|ttf|svg|png|jpg|gif)$/,
use: {
loader: 'url-loader',
options: {
limit: 100000,
name: '[name].[ext]'
}
}
})
return config
},
// Required for successfully importing CSS files (e.g. from PrimeReact)
// See: https://github.com/zeit/next-plugins/issues/273#issuecomment-430597241
webpack: function (config) {
config.module.rules.push({
test: /\.(eot|woff|woff2|ttf|svg|png|jpg|gif)$/,
use: {
loader: 'url-loader',
options: {
limit: 100000,
name: '[name].[ext]',
},
},
});
return config;
},

env: {
VERSION: process.env.CIRCLE_TAG || "SNAPSHOT",
Expand Down
10 changes: 9 additions & 1 deletion components/inspectit-ocelot-configurationserver-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,23 @@
"format": "prettier --check \"./src/**/*.+(js|jsx|json|css|md)\"",
"format:write": "prettier --write \"./src/**/*.+(js|jsx|json|css|md)\"",
"storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook"
"build-storybook": "build-storybook",
"test": "jest",
"test:watch": "jest --watch"
},
"dependencies": {
"@reduxjs/toolkit": "^1.8.2",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^11.2.5",
"@testing-library/user-event": "^14.2.0",
"@zeit/next-css": "^1.0.1",
"ace-builds": "^1.7.1",
"axios": "^0.27.2",
"classnames": "^2.3.1",
"dateformat": "^3.0.3",
"file-saver": "^2.0.5",
"jest": "^29.2.1",
"jest-environment-jsdom": "^28.1.0",
"js-yaml": "^4.1.0",
"jszip": "^3.10.0",
"jwt-decode": "^3.1.2",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { render, screen } from '@testing-library/react';
import LoginView from '../LoginView';
import '@testing-library/jest-dom';
import React from 'react';
import { storeWrapper } from '../../../lib/reduxTestUtils';
import { authentication } from '../../../redux/ducks';
import userEvent from '@testing-library/user-event';

const setup = () => {
const reducers = { authentication };
return render(storeWrapper(<LoginView />, reducers));
};

describe('LoginView', () => {
let container;
//Arrange
beforeEach(() => {
const renderedDom = setup();
container = renderedDom.container;
});

it('renders successfully', () => {
expect(container).toMatchSnapshot();
});

it('disables the login button when username is missing', async () => {
//Arrange
const loginButton = screen.getByRole('button', { name: 'Login' });
const password = screen.getByPlaceholderText('Password');

//Act
await userEvent.type(password, 'password123');

//Assert
expect(loginButton).toBeDisabled();
});

it('disables the login button when password is missing', async () => {
//Arrange
const loginButton = screen.getByRole('button', { name: 'Login' });
const username = screen.getByPlaceholderText('Username');

//Act
await userEvent.type(username, 'userName');

//Assert
expect(loginButton).toBeDisabled();
});

it('enables the login button when username and password are present', async () => {
//Arrange
const loginButton = screen.getByRole('button', { name: 'Login' });
const username = screen.getByPlaceholderText('Username');
const password = screen.getByPlaceholderText('Password');

//Act
await userEvent.type(username, 'userName');
await userEvent.type(password, 'password123');

//Assert
expect(loginButton).toBeEnabled();
});
});
Loading