Skip to content

Commit

Permalink
feat 1398: Introduction of UI tests into the Configuration UI project
Browse files Browse the repository at this point in the history
  • Loading branch information
Philipp Duwe committed May 31, 2022
1 parent ae14c5e commit 0e064c0
Show file tree
Hide file tree
Showing 13 changed files with 6,060 additions and 6,286 deletions.
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
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,80 @@
import { render, screen, within } 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', () => {
//Arrange
beforeEach(() => setup());

it('renders successfully', () => {
//Arrange
const logo = screen.getByRole('img');
const heading1 = screen.getByText('inspectIT Ocelot');
const heading2 = screen.getByText('Configuration Server');
const username = screen.getByRole('textbox', { placeholder: 'Username' });
const password = screen.getByRole('textbox', { placeholder: 'Password' });
const loginButton = screen.getByRole('button', { name: 'Login' });
const footer = screen.getByText(/inspectit ocelot configuration server/i);
const docsLink = within(footer).getByRole('link', { name: 'Docs' });
const githubLink = within(footer).getByRole('link', { name: 'Github' });

//Act - not required

//Assert
expect(logo).toBeInTheDocument();
expect(heading1).toBeInTheDocument();
expect(heading2).toBeInTheDocument();
expect(username).toBeInTheDocument();
expect(password).toBeInTheDocument();
expect(loginButton).toBeInTheDocument();
expect(docsLink).toBeInTheDocument();
expect(githubLink).toBeInTheDocument();
});

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
Expand Up @@ -204,7 +204,6 @@ const ValueEditor = ({ type, value, options, readOnly, onDataChanged }) => {
* Simple value display component.
*/
const SimpleDataView = ({ value, isDefault, error }) => {
const style = {};
return (
<>
<style jsx>{`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,36 +116,36 @@ class FileTree extends React.Component {
return (
<div className="this" onContextMenu={readOnly ? undefined : this.showContextMenu} onKeyDown={readOnly ? undefined : this.onKeyDown}>
<style jsx>{`
.this {
overflow: auto;
flex-grow: 1;
display: flex;
flex-direction: column;
}
.this :global(.cm-tree-icon) {
width: 1.3rem;
height: 1.3rem;
}
.this :global(.cm-tree-label) {
color: #aaa;
}
.this :global(.ocelot-tree-head-orange) {
background: url("${linkPrefix}/static/images/inspectit-ocelot-head_orange.svg") center no-repeat;
background-size: 1rem 1rem;
}
.this :global(.ocelot-tree-head-white) {
background: url("${linkPrefix}/static/images/inspectit-ocelot-head_white.svg") center no-repeat;
background-size: 1rem 1rem;
}
.tree-container {
overflow: auto;
}
.version-banner {
background-color: #ffcc80;
height: 2.45rem;
border-bottom: 1px solid #dddddd;
}
`}</style>
.this {
overflow: auto;
flex-grow: 1;
display: flex;
flex-direction: column;
}
.this :global(.cm-tree-icon) {
width: 1.3rem;
height: 1.3rem;
}
.this :global(.cm-tree-label) {
color: #aaa;
}
.this :global(.ocelot-tree-head-orange) {
background: url('${linkPrefix}/static/images/inspectit-ocelot-head_orange.svg') center no-repeat;
background-size: 1rem 1rem;
}
.this :global(.ocelot-tree-head-white) {
background: url('${linkPrefix}/static/images/inspectit-ocelot-head_white.svg') center no-repeat;
background-size: 1rem 1rem;
}
.tree-container {
overflow: auto;
}
.version-banner {
background-color: #ffcc80;
height: 2.45rem;
border-bottom: 1px solid #dddddd;
}
`}</style>
{selectedVersion && <div className="version-banner" />}
<div className="tree-container">
<ContextMenu model={this.state.contextMenuModel} ref={this.contextMenuRef} />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Provider } from 'react-redux';
import React from 'react';
import { configureStore } from '@reduxjs/toolkit';

/**
* wrapper function for testing purposes that include a redux store
*/
export function storeWrapper(jsx, reducers) {
const store = mockStore(reducers);
return <Provider store={store}>{jsx}</Provider>;
}

/**
* store configuration function that supplies a store with the provided reducers
*/
export function mockStore(reducers) {
return configureStore({
reducer: reducers,
middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: false }),
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,25 @@ const STARTED = ['background: darkorange', 'color: white'].join(';');

const FAILURE = ['background: red', 'color: white'].join(';');

const createLogger = (active = true) => (store) => (next) => (action) => {
if (!active || typeof action === 'function') {
return next(action);
}

const prevState = store.getState();
const result = next(action);
const nextState = store.getState();
logGroupCollapsed(`%c ${action.type} `, determineStyle(action));
logInfo('%cprev state', 'color: darkorange', prevState);
logInfo('%caction payload', 'color: blue', action.payload);
logInfo('%cnext state', 'color: darkgreen', nextState);
logGroupEnd();
return result;
};
const createLogger =
(active = true) =>
(store) =>
(next) =>
(action) => {
if (!active || typeof action === 'function') {
return next(action);
}

const prevState = store.getState();
const result = next(action);
const nextState = store.getState();
logGroupCollapsed(`%c ${action.type} `, determineStyle(action));
logInfo('%cprev state', 'color: darkorange', prevState);
logInfo('%caction payload', 'color: blue', action.payload);
logInfo('%cnext state', 'color: darkgreen', nextState);
logGroupEnd();
return result;
};

export default createLogger;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/* eslint default-param-last: 0 */
export default (initialState) => (reducerMap) => (state = initialState, action) => {
const reducer = reducerMap[action.type];
return reducer ? reducer(state, action) : state;
};
export default (initialState) =>
(reducerMap) =>
(state = initialState, action) => {
const reducer = reducerMap[action.type];
return reducer ? reducer(state, action) : state;
};
Loading

0 comments on commit 0e064c0

Please sign in to comment.