@centreon/js-config 23.10.66 → 24.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,7 +13,6 @@ import tasks from './tasks';
13
13
 
14
14
  interface ConfigurationOptions {
15
15
  cypressFolder?: string;
16
- dockerName?: string;
17
16
  env?: Record<string, unknown>;
18
17
  envFile?: string;
19
18
  isDevelopment?: boolean;
@@ -24,7 +23,6 @@ export default ({
24
23
  specPattern,
25
24
  cypressFolder,
26
25
  isDevelopment,
27
- dockerName,
28
26
  env,
29
27
  envFile
30
28
  }: ConfigurationOptions): Cypress.ConfigOptions => {
@@ -40,7 +38,8 @@ export default ({
40
38
 
41
39
  return defineConfig({
42
40
  chromeWebSecurity: false,
43
- defaultCommandTimeout: 6000,
41
+ defaultCommandTimeout: 20000,
42
+ downloadsFolder: `${resultsFolder}/downloads`,
44
43
  e2e: {
45
44
  excludeSpecPattern: ['*.js', '*.ts', '*.md'],
46
45
  fixturesFolder: 'fixtures',
@@ -48,29 +47,46 @@ export default ({
48
47
  reporterOptions: {
49
48
  configFile: `${__dirname}/reporter-config.js`
50
49
  },
51
- setupNodeEvents: async (on, config) => {
52
- installLogsPrinter(on);
50
+ setupNodeEvents: async (cypressOn, config) => {
51
+ const on = require('cypress-on-fix')(cypressOn)
52
+ installLogsPrinter(on, {
53
+ commandTrimLength: 5000,
54
+ defaultTrimLength: 5000,
55
+ });
56
+ on("task", {
57
+ logVersion(message) {
58
+ console.log(`[LOG]: ${message}`);
59
+ return null;
60
+ },
61
+ });
53
62
  await esbuildPreprocessor(on, config);
54
63
  tasks(on);
55
64
 
56
65
  return plugins(on, config);
57
66
  },
58
67
  specPattern,
59
- supportFile: 'support/e2e.{js,jsx,ts,tsx}'
68
+ supportFile: 'support/e2e.{js,jsx,ts,tsx}',
69
+ testIsolation: true,
60
70
  },
61
71
  env: {
62
72
  ...env,
73
+ DATABASE_IMAGE: 'bitnami/mariadb:10.11',
63
74
  OPENID_IMAGE_VERSION: process.env.MAJOR || '24.04',
75
+ SAML_IMAGE_VERSION: process.env.MAJOR || '24.04',
64
76
  WEB_IMAGE_OS: 'alma9',
65
- WEB_IMAGE_VERSION: webImageVersion,
66
- dockerName: dockerName || 'centreon-dev'
77
+ WEB_IMAGE_VERSION: webImageVersion
67
78
  },
68
79
  execTimeout: 60000,
69
- requestTimeout: 10000,
70
- retries: 0,
80
+ requestTimeout: 20000,
81
+ retries: {
82
+ openMode: 0,
83
+ runMode: 2
84
+ },
71
85
  screenshotsFolder: `${resultsFolder}/screenshots`,
72
- video: true,
86
+ video: isDevelopment,
73
87
  videoCompression: 0,
74
- videosFolder: `${resultsFolder}/videos`
88
+ videosFolder: `${resultsFolder}/videos`,
89
+ viewportHeight: 1080,
90
+ viewportWidth: 1920
75
91
  });
76
92
  };
@@ -3,6 +3,9 @@
3
3
  /* eslint-disable @typescript-eslint/no-var-requires */
4
4
  /* eslint-disable no-param-reassign */
5
5
 
6
+ import fs from 'fs';
7
+ import path from 'path';
8
+
6
9
  export default (
7
10
  on: Cypress.PluginEvents,
8
11
  config: Cypress.PluginConfigOptions
@@ -11,7 +14,7 @@ export default (
11
14
  const width = 1920;
12
15
  const height = 1080;
13
16
 
14
- if (browser.family === 'chromium') {
17
+ if (browser.family === 'chromium' && browser.name !== 'electron') {
15
18
  if (browser.isHeadless) {
16
19
  launchOptions.args.push('--headless=new');
17
20
  }
@@ -23,14 +26,38 @@ export default (
23
26
  launchOptions.args.push('--hide-scrollbars');
24
27
  launchOptions.args.push('--mute-audio');
25
28
 
26
- launchOptions.args.push(`--window-size=${width},${height}`);
27
-
28
29
  // force screen to be non-retina and just use our given resolution
29
30
  launchOptions.args.push('--force-device-scale-factor=1');
31
+
32
+ launchOptions.args.push(`--window-size=${width},${height}`);
30
33
  }
31
34
 
32
35
  return launchOptions;
33
36
  });
34
37
 
38
+ on('after:run', (results) => {
39
+ const testRetries: { [key: string]: Number } = {};
40
+ if ('runs' in results) {
41
+ results.runs.forEach((run) => {
42
+ run.tests.forEach((test) => {
43
+ console.log(test)
44
+ if (test.attempts && test.attempts.length > 1 && test.state === 'passed') {
45
+ const testTitle = test.title.join(' > '); // Convert the array to a string
46
+ testRetries[testTitle] = test.attempts.length - 1;
47
+ }
48
+ });
49
+ });
50
+ }
51
+
52
+ // Save the testRetries object to a file in the e2e/results directory
53
+ const resultFilePath = path.join(
54
+ __dirname,
55
+ '../../../../tests/e2e/results',
56
+ 'retries.json'
57
+ );
58
+
59
+ fs.writeFileSync(resultFilePath, JSON.stringify(testRetries, null, 2));
60
+ });
61
+
35
62
  return config;
36
- };
63
+ };
@@ -1,10 +1,40 @@
1
+ /* eslint-disable no-console */
1
2
  import { execSync } from 'child_process';
2
3
  import { existsSync, mkdirSync } from 'fs';
4
+ import path from 'path';
3
5
 
4
- import Docker from 'dockerode';
6
+ import tar from 'tar-fs';
7
+ import {
8
+ DockerComposeEnvironment,
9
+ GenericContainer,
10
+ StartedDockerComposeEnvironment,
11
+ StartedTestContainer,
12
+ Wait,
13
+ getContainerRuntimeClient
14
+ } from 'testcontainers';
15
+ import { createConnection } from 'mysql2/promise';
16
+
17
+ interface Containers {
18
+ [key: string]: StartedTestContainer;
19
+ }
5
20
 
6
21
  export default (on: Cypress.PluginEvents): void => {
7
- const docker = new Docker();
22
+ let dockerEnvironment: StartedDockerComposeEnvironment | null = null;
23
+ const containers: Containers = {};
24
+
25
+ const getContainer = (containerName): StartedTestContainer => {
26
+ let container;
27
+
28
+ if (dockerEnvironment !== null) {
29
+ container = dockerEnvironment.getContainer(`${containerName}-1`);
30
+ } else if (containers[containerName]) {
31
+ container = containers[containerName];
32
+ } else {
33
+ throw new Error(`Cannot get container ${containerName}`);
34
+ }
35
+
36
+ return container;
37
+ };
8
38
 
9
39
  interface PortBinding {
10
40
  destination: number;
@@ -12,6 +42,7 @@ export default (on: Cypress.PluginEvents): void => {
12
42
  }
13
43
 
14
44
  interface StartContainerProps {
45
+ command?: string;
15
46
  image: string;
16
47
  name: string;
17
48
  portBindings: Array<PortBinding>;
@@ -22,6 +53,46 @@ export default (on: Cypress.PluginEvents): void => {
22
53
  }
23
54
 
24
55
  on('task', {
56
+ copyFromContainer: async ({ destination, serviceName, source }) => {
57
+ try {
58
+ const container = getContainer(serviceName);
59
+
60
+ await container
61
+ .copyArchiveFromContainer(source)
62
+ .then((archiveStream) => {
63
+ return new Promise<void>((resolve) => {
64
+ const dest = tar.extract(destination);
65
+ archiveStream.pipe(dest);
66
+ dest.on('finish', resolve);
67
+ });
68
+ });
69
+ } catch (error) {
70
+ console.error(error);
71
+ }
72
+
73
+ return null;
74
+ },
75
+ copyToContainer: async ({ destination, serviceName, source, type }) => {
76
+ const container = getContainer(serviceName);
77
+
78
+ if (type === 'directory') {
79
+ await container.copyDirectoriesToContainer([
80
+ {
81
+ source,
82
+ target: destination
83
+ }
84
+ ]);
85
+ } else if (type === 'file') {
86
+ await container.copyFilesToContainer([
87
+ {
88
+ source,
89
+ target: destination
90
+ }
91
+ ]);
92
+ }
93
+
94
+ return null;
95
+ },
25
96
  createDirectory: async (directoryPath: string) => {
26
97
  if (!existsSync(directoryPath)) {
27
98
  mkdirSync(directoryPath, { recursive: true });
@@ -29,70 +100,150 @@ export default (on: Cypress.PluginEvents): void => {
29
100
 
30
101
  return null;
31
102
  },
103
+ execInContainer: async ({ command, name }) => {
104
+ const { exitCode, output } = await getContainer(name).exec([
105
+ 'bash',
106
+ '-c',
107
+ `${command}${command.match(/[\n\r]/) ? '' : ' 2>&1'}`
108
+ ]);
109
+
110
+ return { exitCode, output };
111
+ },
112
+ getContainerId: (containerName: string) =>
113
+ getContainer(containerName).getId(),
114
+ getContainerIpAddress: (containerName: string) => {
115
+ const container = getContainer(containerName);
116
+
117
+ const networkNames = container.getNetworkNames();
118
+
119
+ return container.getIpAddress(networkNames[0]);
120
+ },
121
+ getContainersLogs: async () => {
122
+ try {
123
+ const { dockerode } = (await getContainerRuntimeClient()).container;
124
+ const loggedContainers = await dockerode.listContainers();
125
+
126
+ return loggedContainers.reduce((acc, container) => {
127
+ const containerName = container.Names[0].replace('/', '');
128
+ acc[containerName] = execSync(`docker logs -t ${container.Id}`, {
129
+ stdio: 'pipe'
130
+ }).toString('utf8');
131
+
132
+ return acc;
133
+ }, {});
134
+ } catch (error) {
135
+ console.warn('Cannot get containers logs');
136
+ console.warn(error);
137
+
138
+ return null;
139
+ }
140
+ },
141
+ requestOnDatabase: async ({ database, query }) => {
142
+ let container: StartedTestContainer | null = null;
143
+
144
+ if (dockerEnvironment !== null) {
145
+ container = dockerEnvironment.getContainer('db-1');
146
+ } else {
147
+ container = getContainer('web');
148
+ }
149
+
150
+ const client = await createConnection({
151
+ database,
152
+ host: container.getHost(),
153
+ password: 'centreon',
154
+ port: container.getMappedPort(3306),
155
+ user: 'centreon'
156
+ });
157
+
158
+ const [rows, fields] = await client.execute(query);
159
+
160
+ await client.end();
161
+
162
+ return [rows, fields];
163
+ },
32
164
  startContainer: async ({
165
+ command,
33
166
  image,
34
167
  name,
35
168
  portBindings = []
36
169
  }: StartContainerProps) => {
37
- const imageList = execSync(
38
- 'docker image list --format "{{.Repository}}:{{.Tag}}"'
39
- ).toString('utf8');
40
-
41
- if (
42
- !imageList.match(
43
- new RegExp(
44
- `^${image.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')}`,
45
- 'm'
46
- )
47
- )
48
- ) {
49
- execSync(`docker pull ${image}`);
50
- }
170
+ let container = await new GenericContainer(image).withName(name);
51
171
 
52
- const webContainers = await docker.listContainers({
53
- all: true,
54
- filters: { name: [name] }
172
+ portBindings.forEach(({ source, destination }) => {
173
+ container = container.withExposedPorts({
174
+ container: source,
175
+ host: destination
176
+ });
55
177
  });
56
- if (webContainers.length) {
57
- return webContainers[0];
58
- }
59
178
 
60
- const container = await docker.createContainer({
61
- AttachStderr: true,
62
- AttachStdin: false,
63
- AttachStdout: true,
64
- ExposedPorts: portBindings.reduce((accumulator, currentValue) => {
65
- accumulator[`${currentValue.source}/tcp`] = {};
66
-
67
- return accumulator;
68
- }, {}),
69
- HostConfig: {
70
- PortBindings: portBindings.reduce((accumulator, currentValue) => {
71
- accumulator[`${currentValue.source}/tcp`] = [
72
- {
73
- HostIP: '127.0.0.1',
74
- HostPort: `${currentValue.destination}`
75
- }
76
- ];
77
-
78
- return accumulator;
79
- }, {})
80
- },
81
- Image: image,
82
- OpenStdin: false,
83
- StdinOnce: false,
84
- Tty: true,
85
- name
86
- });
179
+ if (command) {
180
+ container
181
+ .withCommand(['bash', '-c', command])
182
+ .withWaitStrategy(Wait.forSuccessfulCommand('ls'));
183
+ }
87
184
 
88
- await container.start();
185
+ containers[name] = await container.start();
89
186
 
90
187
  return container;
91
188
  },
189
+ startContainers: async ({
190
+ composeFile,
191
+ databaseImage,
192
+ openidImage,
193
+ profiles,
194
+ samlImage,
195
+ webImage
196
+ }) => {
197
+ try {
198
+ const composeFileDir = path.dirname(composeFile);
199
+ const composeFileName = path.basename(composeFile);
200
+
201
+ dockerEnvironment = await new DockerComposeEnvironment(
202
+ composeFileDir,
203
+ composeFileName
204
+ )
205
+ .withEnvironment({
206
+ MYSQL_IMAGE: databaseImage,
207
+ OPENID_IMAGE: openidImage,
208
+ SAML_IMAGE: samlImage,
209
+ WEB_IMAGE: webImage
210
+ })
211
+ .withProfiles(...profiles)
212
+ .withWaitStrategy(
213
+ 'web-1',
214
+ Wait.forAll([
215
+ Wait.forHealthCheck(),
216
+ Wait.forLogMessage('Centreon is ready')
217
+ ])
218
+ )
219
+ .up();
220
+
221
+ return null;
222
+ } catch (error) {
223
+ if (error instanceof Error) {
224
+ console.error(error.message);
225
+ }
226
+
227
+ throw error;
228
+ }
229
+ },
92
230
  stopContainer: async ({ name }: StopContainerProps) => {
93
- const container = await docker.getContainer(name);
94
- await container.kill();
95
- await container.remove();
231
+ if (containers[name]) {
232
+ const container = containers[name];
233
+
234
+ await container.stop();
235
+
236
+ delete containers[name];
237
+ }
238
+
239
+ return null;
240
+ },
241
+ stopContainers: async () => {
242
+ if (dockerEnvironment !== null) {
243
+ await dockerEnvironment.down();
244
+
245
+ dockerEnvironment = null;
246
+ }
96
247
 
97
248
  return null;
98
249
  },
package/package.json CHANGED
@@ -1,60 +1,66 @@
1
1
  {
2
- "name": "@centreon/js-config",
3
- "description": "Centreon Frontend shared build configuration",
4
- "version": "23.10.66",
5
- "repository": {
6
- "type": "git",
7
- "url": "git+https://github.com/centreon/centreon-frontend.git"
8
- },
9
- "keywords": [
10
- "centreon",
11
- "eslint"
12
- ],
13
- "author": "centreon@centreon.com",
14
- "license": "GPL-2.0",
15
- "bugs": {
16
- "url": "https://github.com/centreon/centreon-frontend/issues"
17
- },
18
- "devDependencies": {
19
- "@tsconfig/node16": "^16.1.1",
20
- "eslint": "^8.17.0",
21
- "eslint-config-airbnb": "19.0.4",
22
- "eslint-config-prettier": "^8.5.0",
23
- "eslint-import-resolver-alias": "^1.1.2",
24
- "eslint-import-resolver-typescript": "^3.5.5",
25
- "eslint-plugin-babel": "^5.3.1",
26
- "eslint-plugin-hooks": "^0.4.3",
27
- "eslint-plugin-import": "^2.26.0",
28
- "eslint-plugin-jest": "^26.1.5",
29
- "eslint-plugin-jsx-a11y": "^6.5.1",
30
- "eslint-plugin-node": "^11.1.0",
31
- "eslint-plugin-prefer-arrow-functions": "^3.1.4",
32
- "eslint-plugin-prettier": "^4.0.0",
33
- "eslint-plugin-react": "^7.29.4",
34
- "eslint-plugin-react-hooks": "^4.5.0",
35
- "eslint-plugin-sort-keys-fix": "^1.1.2",
36
- "eslint-plugin-typescript-sort-keys": "^2.1.0"
37
- },
38
- "homepage": "https://github.com/centreon/centreon-frontend#readme",
39
- "files": [
40
- "eslint",
41
- "jest",
42
- "tsconfig",
43
- "webpack",
44
- "cypress"
45
- ],
46
- "dependencies": {
47
- "@badeball/cypress-cucumber-preprocessor": "^19.1.0",
48
- "@bahmutov/cypress-esbuild-preprocessor": "^2.2.0",
49
- "@esbuild-plugins/node-globals-polyfill": "^0.2.3",
50
- "@esbuild-plugins/node-modules-polyfill": "^0.2.2",
51
- "@types/cypress-cucumber-preprocessor": "^4.0.5",
52
- "@types/dockerode": "^3.3.23",
53
- "cypress-multi-reporters": "^1.6.4",
54
- "cypress-terminal-report": "^5.3.9",
55
- "dockerode": "^4.0.0",
56
- "dotenv": "^16.3.1",
57
- "esbuild": "^0.19.5",
58
- "mochawesome": "^7.1.3"
59
- }
2
+ "name": "@centreon/js-config",
3
+ "description": "Centreon Frontend shared build configuration",
4
+ "version": "24.4.0",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/centreon/centreon-frontend.git"
8
+ },
9
+ "keywords": [
10
+ "centreon",
11
+ "eslint"
12
+ ],
13
+ "author": "centreon@centreon.com",
14
+ "license": "GPL-2.0",
15
+ "bugs": {
16
+ "url": "https://github.com/centreon/centreon-frontend/issues"
17
+ },
18
+ "homepage": "https://github.com/centreon/centreon-frontend#readme",
19
+ "files": [
20
+ "eslint",
21
+ "jest",
22
+ "tsconfig",
23
+ "webpack",
24
+ "cypress"
25
+ ],
26
+ "peerDependencies": {
27
+ "eslint": "^8.53.0",
28
+ "prettier": "^3.0.0"
29
+ },
30
+ "dependencies": {
31
+ "@badeball/cypress-cucumber-preprocessor": "^20.0.1",
32
+ "@bahmutov/cypress-esbuild-preprocessor": "^2.2.0",
33
+ "@esbuild-plugins/node-globals-polyfill": "^0.2.3",
34
+ "@esbuild-plugins/node-modules-polyfill": "^0.2.2",
35
+ "@tsconfig/node16": "^16.1.1",
36
+ "@tsconfig/node20": "^20.1.2",
37
+ "@types/cypress-cucumber-preprocessor": "^4.0.5",
38
+ "cypress": "^13.6.4",
39
+ "cypress-multi-reporters": "^1.6.4",
40
+ "cypress-terminal-report": "^6.0.0",
41
+ "cypress-wait-until": "^3.0.1",
42
+ "dotenv": "^16.4.1",
43
+ "esbuild": "^0.20.0",
44
+ "eslint": "^8.53.0",
45
+ "eslint-config-airbnb": "19.0.4",
46
+ "eslint-config-prettier": "^8.5.0",
47
+ "eslint-import-resolver-alias": "^1.1.2",
48
+ "eslint-import-resolver-typescript": "^3.5.5",
49
+ "eslint-plugin-babel": "^5.3.1",
50
+ "eslint-plugin-hooks": "^0.4.3",
51
+ "eslint-plugin-import": "^2.26.0",
52
+ "eslint-plugin-jest": "^26.1.5",
53
+ "eslint-plugin-jsx-a11y": "^6.5.1",
54
+ "eslint-plugin-node": "^11.1.0",
55
+ "eslint-plugin-prefer-arrow-functions": "^3.1.4",
56
+ "eslint-plugin-prettier": "^5.0.0",
57
+ "eslint-plugin-react": "^7.29.4",
58
+ "eslint-plugin-react-hooks": "^4.5.0",
59
+ "eslint-plugin-sort-keys-fix": "^1.1.2",
60
+ "eslint-plugin-typescript-sort-keys": "^2.1.0",
61
+ "mochawesome": "^7.1.3",
62
+ "mysql2": "^3.9.1",
63
+ "tar-fs": "^3.0.4",
64
+ "testcontainers": "^10.7.1"
65
+ }
60
66
  }
@@ -0,0 +1,12 @@
1
+ {
2
+ "extends": "@tsconfig/node20/tsconfig.json",
3
+ "compilerOptions": {
4
+ "sourceMap": true,
5
+ "allowJs": true,
6
+ "strictNullChecks": false,
7
+ "declaration": false,
8
+ "esModuleInterop": true,
9
+ "strict": true,
10
+ "types": ["@types/jest", "node"]
11
+ }
12
+ }
@@ -34,6 +34,7 @@ const getBaseConfiguration = ({
34
34
  shared: [
35
35
  {
36
36
  '@centreon/ui-context': {
37
+ requiredVersion: '24.x',
37
38
  singleton: true
38
39
  }
39
40
  },
@@ -62,7 +63,7 @@ const getBaseConfiguration = ({
62
63
  },
63
64
  {
64
65
  'react-i18next': {
65
- requiredVersion: '11.x',
66
+ requiredVersion: '14.x',
66
67
  singleton: true
67
68
  }
68
69
  },