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 1 commit
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
6 changes: 5 additions & 1 deletion .github/workflows/configuration_ui_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
@@ -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)

#### Storybook

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.
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);
65 changes: 32 additions & 33 deletions components/inspectit-ocelot-configurationserver-ui/next.config.js
Original file line number Diff line number Diff line change
@@ -1,42 +1,41 @@
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
exportTrailingSlash: true,
// Each page will be exported as a directory
exportTrailingSlash: 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",
BUILD_DATE: new Date().toUTCString()
}
})
env: {
VERSION: process.env.CIRCLE_TAG || 'SNAPSHOT',
BUILD_DATE: new Date().toUTCString(),
},
});
11 changes: 10 additions & 1 deletion components/inspectit-ocelot-configurationserver-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +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.4.5",
"axios": "^0.26.1",
"babel-jest": "^28.1.0",
"classnames": "^2.2.6",
"dateformat": "^3.0.3",
"jest": "^28.1.0",
"jest-environment-jsdom": "^28.1.0",
"js-yaml": "^3.13.1",
"jwt-decode": "^2.2.0",
"lodash": "^4.17.21",
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();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`LoginView renders successfully 1`] = `
<div>
<div
class="jsx-3064013252 this"
>
<div
class="jsx-128945688 this"
>
<div
class="jsx-3380344897 this"
>
<img
class="jsx-3380344897 ocelot-head"
src="/static/images/inspectit-ocelot.svg"
/>
<div
class="jsx-3380344897 text-ocelot"
>
inspectIT Ocelot
</div>
<div
class="jsx-3380344897 text-server"
>
Configuration Server
</div>
</div>
<div
class="jsx-128945688 p-inputgroup input"
>
<span
class="jsx-128945688 p-inputgroup-addon"
>
<i
class="jsx-128945688 pi pi-user"
/>
</span>
<input
class="p-inputtext p-component"
placeholder="Username"
style="width: 100%;"
value=""
/>
</div>
<div
class="jsx-128945688 p-inputgroup input"
>
<span
class="jsx-128945688 p-inputgroup-addon"
>
<i
class="jsx-128945688 pi pi-lock"
/>
</span>
<input
class="p-inputtext p-component"
placeholder="Password"
style="width: 100%;"
type="password"
value=""
/>
</div>
<div
class="jsx-128945688 input"
>
<button
class="p-button p-component p-button-text-only p-disabled"
disabled=""
style="width: 100%;"
>
<span
class="p-button-text p-c"
>
Login
</span>
</button>
</div>
</div>
<div
class="jsx-3064013252 build-information"
>
<span
class="jsx-3064013252"
>
inspectIT Ocelot Configuration Server v
(Build Date:
) |

<a
class="jsx-3064013252"
href="http://docs.inspectit.rocks/"
target="_blank"
>
Docs
</a>

|

<a
class="jsx-3064013252"
href="https://github.com/inspectIT/inspectit-ocelot"
target="_blank"
>
Github
</a>
</span>
</div>
</div>
</div>
`;
Loading