Skip to content

Commit f95f15f

Browse files
committed
feat: add Playwright testing setup and demo tests
Introduce Playwright for end-to-end testing with a demo test suite. Add configuration files and scripts for running tests, including coverage reporting. Update .gitignore to exclude test reports and add a mock environment file. Signed-off-by: Paul PLANCQ <paul.plancq@outlook.fr>
1 parent eb861e6 commit f95f15f

16 files changed

+2177
-480
lines changed

package-lock.json

Lines changed: 1730 additions & 451 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

react/.env.mock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
FRONT_MOCK_ENABLE=true

react/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ dist-ssr
1313
*.local
1414
coverage/
1515
junit-report.xml
16+
test-reports/
1617

1718
# Editor directories and files
1819
.vscode/*

react/mrc.e2e.config.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright The Microcks Authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { CoverageReportOptions } from 'monocart-coverage-reports';
18+
19+
export const coverageOptions: CoverageReportOptions = {
20+
enabled: process.env.CI ?? process.env.ENABLED_COVERAGE,
21+
name: 'Playwright Coverage Report',
22+
outputDir: './test-reports/e2e/coverage',
23+
baseDir: './src/',
24+
reports: [
25+
'text',
26+
'text-summary',
27+
['html', { subdir: 'html-coverage' }],
28+
['lcovonly', { file: 'lcov-coverage.info' }],
29+
['cobertura', { file: 'cobertura-coverage.xml' }],
30+
],
31+
sourceFilter: {
32+
'**/node_modules/**': false,
33+
'**/mocks/**': false,
34+
'**/*.{test,spec,steps}.{js,jsx,ts,tsx}': false,
35+
'vitest.setup.ts': false,
36+
'**/*.{js,jsx,ts,tsx}': true,
37+
},
38+
watermarks: {
39+
statements: [80, 90],
40+
branches: [70, 80],
41+
functions: [80, 90],
42+
lines: [80, 90],
43+
bytes: [80, 90],
44+
},
45+
};

react/package.json

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,16 @@
1515
"lint:prettier": "prettier \"src/**/*.!(js|jsx|mjs|ts|tsx|scss|css)\" --check --ignore-unknown",
1616
"lint:prettier:fix": "prettier \"src/**/*.!(js|jsx|mjs|ts|tsx|scss|css)\" --write --ignore-unknown",
1717
"lint:tsc": "tsc --noEmit",
18-
"test": "vitest run",
19-
"test:watch": "vitest",
20-
"add-license-header": "node scripts/addLicenseHeader.js"
18+
"test": "node scripts/test.js",
19+
"test:unit": "vitest run",
20+
"test:unit:watch": "vitest",
21+
"test:e2e": "node scripts/playwright.js test",
22+
"test:e2e:watch": "node scripts/playwright.js test --watch",
23+
"test:e2e:ui": "node scripts/playwright.js test --ui",
24+
"add-license-header": "node scripts/addLicenseHeader.js",
25+
"playwright:install": "playwright install",
26+
"playwright:show-report": "playwright show-report",
27+
"playwright:codegen": "playwright codegen"
2128
},
2229
"dependencies": {
2330
"@pplancq/svg-react": "^2.0.2",
@@ -34,10 +41,13 @@
3441
"tw-animate-css": "^1.2.8"
3542
},
3643
"devDependencies": {
44+
"@msw/playwright": "^0.4.2",
45+
"@playwright/test": "^1.57.0",
3746
"@pplancq/eslint-config": "^4.2.0",
3847
"@pplancq/prettier-config": "^1.2.6",
3948
"@pplancq/stylelint-config": "^4.0.1",
4049
"@rsbuild/core": "^1.3.22",
50+
"@rsbuild/plugin-eslint": "^1.2.0",
4151
"@rsbuild/plugin-react": "^1.3.2",
4252
"@tailwindcss/postcss": "^4.1.10",
4353
"@testing-library/jest-dom": "^6.6.3",
@@ -51,11 +61,13 @@
5161
"concurrently": "^9.1.2",
5262
"eslint": "^9.30.0",
5363
"eslint-plugin-prettier": "^5.5.0",
54-
"glob": "^11.0.3",
5564
"jsdom": "^26.1.0",
65+
"monocart-coverage-reports": "^2.12.9",
5666
"msw": "^2.10.2",
67+
"nodemon": "^3.1.11",
5768
"prettier": "^3.5.3",
5869
"stylelint-prettier": "^5.0.3",
70+
"stylelint-webpack-plugin": "^5.0.1",
5971
"typescript": "~5.7.2",
6072
"vite-tsconfig-paths": "^5.1.4",
6173
"vitest": "^3.2.4"

react/playwright.config.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright The Microcks Authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { defineConfig, devices } from '@playwright/test';
18+
19+
const appUrl = 'http://localhost:3000';
20+
21+
export default defineConfig({
22+
testDir: 'tests',
23+
outputDir: './test-reports/e2e/test-results',
24+
fullyParallel: true,
25+
forbidOnly: Boolean(process.env.CI),
26+
retries: process.env.CI ? 2 : undefined,
27+
workers: process.env.CI ? 1 : undefined,
28+
reporter: [
29+
['list'],
30+
['html', { open: 'never', outputFolder: 'test-reports/e2e/html-report' }],
31+
['junit', { outputFile: 'test-reports/e2e/junit-report.xml' }],
32+
],
33+
globalSetup: 'tests/utils/playwright.globalSetup.ts',
34+
globalTeardown: 'tests/utils/playwright.globalTeardown.ts',
35+
use: {
36+
baseURL: appUrl,
37+
trace: 'retain-on-failure',
38+
screenshot: 'only-on-failure',
39+
},
40+
projects: [
41+
{
42+
name: 'chromium',
43+
use: { ...devices['Desktop Chrome'] },
44+
},
45+
{
46+
name: 'firefox',
47+
use: { ...devices['Firefox Desktop'] },
48+
},
49+
{
50+
name: 'webkit',
51+
use: { ...devices['Desktop Safari'] },
52+
},
53+
],
54+
webServer: {
55+
command: 'npm run build -- --env-mode test && npm run preview',
56+
url: appUrl,
57+
stdout: 'pipe',
58+
reuseExistingServer: !process.env.CI,
59+
},
60+
});

react/rsbuild.config.ts

Lines changed: 72 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,32 +14,81 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { defineConfig } from '@rsbuild/core';
17+
import { defineConfig, loadEnv } from '@rsbuild/core';
18+
import { pluginEslint } from '@rsbuild/plugin-eslint';
1819
import { pluginReact } from '@rsbuild/plugin-react';
20+
import StylelintPlugin from 'stylelint-webpack-plugin';
21+
import packageJson from './package.json';
1922

20-
export default defineConfig({
21-
plugins: [pluginReact()],
22-
source: {
23-
entry: {
24-
index: 'src/main.ts',
23+
const resolveModule = (module: string) => {
24+
try {
25+
require.resolve(module);
26+
return true;
27+
} catch {
28+
return false;
29+
}
30+
};
31+
32+
const { publicVars } = loadEnv({ prefixes: [process.env.ENV_PREFIX ?? 'FRONT_'] });
33+
34+
const publicUrl = process.env.PUBLIC_URL ?? (packageJson as { homepage?: string }).homepage ?? '/';
35+
const publicPath = new URL(publicUrl.endsWith('/') ? publicUrl : `${publicUrl}/`, 'http://localhost').pathname;
36+
const disableSourceMap = (process.env.DISABLE_SOURCE_MAP ?? 'false') === 'true' ? false : 'source-map';
37+
const disableStyleLintPlugin =
38+
(process.env.DISABLE_STYLELINT_PLUGIN ?? 'false') === 'true' || !resolveModule('stylelint');
39+
40+
export default defineConfig(({ env }) => {
41+
const isProduction = env === 'production';
42+
43+
return {
44+
plugins: [
45+
pluginReact(),
46+
!isProduction &&
47+
pluginEslint({
48+
enable: (process.env.DISABLE_ESLINT_PLUGIN ?? 'false') === 'true' || !resolveModule('eslint'),
49+
}),
50+
].filter(Boolean),
51+
source: {
52+
entry: {
53+
index: 'src/main.ts',
54+
},
55+
define: publicVars,
2556
},
26-
},
27-
output: {
28-
distPath: {
29-
root: 'dist',
57+
output: {
58+
assetPrefix: publicPath,
59+
sourceMap: {
60+
js: isProduction ? disableSourceMap : 'cheap-module-source-map',
61+
css: (process.env.DISABLE_SOURCE_MAP ?? 'false') !== 'true',
62+
},
63+
distPath: {
64+
root: 'dist',
65+
},
66+
copy: [
67+
{
68+
from: 'public',
69+
to: '.',
70+
},
71+
],
72+
},
73+
server: {
74+
port: parseInt(process.env.PORT ?? '3000', 10),
75+
open: (process.env.BROWSER ?? 'false') === 'true',
3076
},
31-
copy: [
32-
{
33-
from: 'public',
34-
to: '.',
77+
tools: {
78+
rspack: {
79+
plugins: [
80+
!disableStyleLintPlugin &&
81+
new StylelintPlugin({
82+
extensions: ['css', 'scss', 'sass'],
83+
stylelintPath: require.resolve('stylelint'),
84+
failOnError: isProduction,
85+
context: 'src',
86+
}),
87+
].filter(Boolean),
3588
},
36-
],
37-
},
38-
server: {
39-
port: 3000,
40-
open: false,
41-
},
42-
html: {
43-
template: './index.html',
44-
},
89+
},
90+
html: {
91+
template: './index.html',
92+
},
93+
};
4594
});

react/scripts/playwright.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright The Microcks Authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { spawnSync } from 'node:child_process';
18+
import { platform } from 'node:process';
19+
20+
const runCommand = (cmd, args = []) => {
21+
console.info(`> ${cmd} ${args.join(' ')}`);
22+
const result = spawnSync(cmd, args, { cwd: process.cwd(), stdio: 'inherit', shell: platform === 'win32' });
23+
if (result.status !== 0) {
24+
throw result;
25+
}
26+
};
27+
28+
const main = async () => {
29+
const args = process.argv.slice(2);
30+
31+
let playwrightArgs = [...args];
32+
33+
if (args.indexOf('--watch') !== -1) {
34+
const filteredArgs = args.filter(a => a !== '--watch');
35+
runCommand('npx', [
36+
'nodemon',
37+
'--watch',
38+
'./tests/',
39+
'--ext',
40+
'js,jx,ts,tsx',
41+
'--exec',
42+
['node', 'scripts/playwright.js', ...filteredArgs].join(' '),
43+
]);
44+
return;
45+
}
46+
47+
if (playwrightArgs.indexOf('--coverage') !== -1) {
48+
process.env.ENABLED_COVERAGE = 'true';
49+
playwrightArgs = playwrightArgs.filter(p => p !== '--coverage');
50+
}
51+
52+
runCommand('npx', ['playwright', ...playwrightArgs]);
53+
};
54+
55+
main().catch(e => process.exit(e.status));

react/scripts/test.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright The Microcks Authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
// eslint-disable-next-line import/no-extraneous-dependencies
18+
import concurrently from 'concurrently';
19+
20+
const args = process.argv.slice(2);
21+
22+
let commands = ['npm:test:unit', 'npm:test:e2e'];
23+
24+
if (args.includes('--coverage')) {
25+
commands = commands.map(command => `${command} -- --coverage`);
26+
}
27+
28+
const { result } = concurrently(commands, {
29+
prefixColors: 'auto',
30+
maxProcesses: 1,
31+
});
32+
33+
result.then(
34+
() => process.exit(0),
35+
() => process.exit(1),
36+
);

react/tests/demo.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright The Microcks Authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { expect } from '@playwright/test';
18+
import { describe, it } from './fixture/playwright.fixture';
19+
20+
describe('Demo Test Suite', () => {
21+
it('should load the homepage and check title', async ({ page }) => {
22+
await page.goto('/');
23+
const title = page.getByRole('heading', { name: 'Welcome to hub.microcks.io', level: 1 });
24+
25+
await expect(title).toBeVisible();
26+
});
27+
});

0 commit comments

Comments
 (0)