@centreon/js-config 24.4.32 → 24.4.34

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.
@@ -1,203 +1,203 @@
1
- /* eslint-disable @typescript-eslint/no-namespace */
2
- import React from 'react';
3
-
4
- import { mount } from 'cypress/react18';
5
- import { equals, isNil } from 'ramda';
6
-
7
- import { Box, CssBaseline } from '@mui/material';
8
-
9
- import { ThemeProvider } from '@centreon/ui';
10
-
11
- import '@testing-library/cypress/add-commands';
12
- import 'cypress-msw-interceptor';
13
- import 'cypress-real-events';
14
-
15
- import disableMotion from './disableCssTransitions';
16
-
17
- interface MountProps {
18
- Component: React.ReactNode;
19
- options?: object;
20
- }
21
- interface Resolution {
22
- height: number;
23
- width: number;
24
- }
25
-
26
- interface MakeSnapshotWithCustomResolution {
27
- resolution: Resolution;
28
- title: string;
29
- }
30
-
31
- export enum Method {
32
- DELETE = 'DELETE',
33
- GET = 'GET',
34
- PATCH = 'PATCH',
35
- POST = 'POST',
36
- PUT = 'PUT'
37
- }
38
-
39
- Cypress.Commands.add('mount', ({ Component, options = {} }) => {
40
- const wrapped = (
41
- <ThemeProvider>
42
- <Box
43
- sx={{
44
- backgroundColor: 'background.paper',
45
- height: '100%',
46
- width: '100%'
47
- }}
48
- >
49
- {Component}
50
- </Box>
51
- <CssBaseline />
52
- </ThemeProvider>
53
- );
54
-
55
- return mount(wrapped, options);
56
- });
57
-
58
- interface Query {
59
- name: string;
60
- value: string;
61
- }
62
-
63
- export interface InterceptAPIRequestProps<T> {
64
- alias: string;
65
- delay?: number;
66
- method: Method;
67
- path: string;
68
- query?: Query;
69
- response?: T | Array<T>;
70
- statusCode?: number;
71
- }
72
-
73
- Cypress.Commands.add(
74
- 'interceptAPIRequest',
75
- <T extends object>({
76
- method,
77
- path,
78
- response,
79
- alias,
80
- query,
81
- statusCode = 200,
82
- delay = 500
83
- }: InterceptAPIRequestProps<T>): void => {
84
- cy.interceptRequest(
85
- method,
86
- path.replace('./', '**'),
87
- (req, res, ctx) => {
88
- const getQuery = req?.url?.searchParams?.get(query?.name);
89
- if (query && equals(query.value, getQuery)) {
90
- return res(
91
- ctx.delay(delay),
92
- ctx.json(response),
93
- ctx.status(statusCode)
94
- );
95
- }
96
- if (!getQuery && isNil(query)) {
97
- return res(
98
- ctx.delay(delay),
99
- ctx.json(response),
100
- ctx.status(statusCode)
101
- );
102
- }
103
-
104
- return null;
105
- },
106
- alias
107
- );
108
- }
109
- );
110
-
111
- interface MoveSortableElementProps {
112
- direction: 'up' | 'down' | 'left' | 'right';
113
- element: Cypress.Chainable<JQuery<HTMLElement>>;
114
- times?: number;
115
- }
116
-
117
- Cypress.Commands.add(
118
- 'moveSortableElement',
119
- ({ element, direction, times = 1 }: MoveSortableElementProps): void => {
120
- const key = `{${direction}arrow}`;
121
-
122
- element.type(' ', {
123
- force: true,
124
- scrollBehavior: false
125
- });
126
-
127
- Array.from({ length: times }).forEach(() => {
128
- element.eq(-1).type(key, {
129
- scrollBehavior: false
130
- });
131
- });
132
- element.eq(-1).type(' ', {
133
- scrollBehavior: false
134
- });
135
- }
136
- );
137
-
138
- Cypress.Commands.add(
139
- 'moveSortableElementUsingAriaLabel',
140
- ({ ariaLabel, direction }): void => {
141
- const key = `{${direction}arrow}`;
142
-
143
- cy.findByLabelText(ariaLabel).type(' ', {
144
- force: true,
145
- scrollBehavior: false
146
- });
147
- cy.findAllByLabelText(ariaLabel).eq(-1).type(key, {
148
- scrollBehavior: false
149
- });
150
- cy.findAllByLabelText(ariaLabel).eq(-1).type(' ', {
151
- scrollBehavior: false
152
- });
153
- }
154
- );
155
-
156
- Cypress.Commands.add('adjustViewport', () => cy.viewport(1280, 590));
157
-
158
- Cypress.Commands.add('makeSnapshot', (title?: string) => {
159
- cy.adjustViewport();
160
- cy.matchImageSnapshot(title);
161
- });
162
-
163
- Cypress.Commands.add(
164
- 'makeSnapshotWithCustomResolution',
165
- ({ title, resolution }: MakeSnapshotWithCustomResolution) => {
166
- const { width, height } = resolution;
167
- cy.viewport(width, height);
168
- cy.matchImageSnapshot(title);
169
- }
170
- );
171
-
172
- Cypress.Commands.add('cssDisableMotion', (): void => {
173
- Cypress.on('window:before:load', (cyWindow) => {
174
- disableMotion(cyWindow);
175
- });
176
- });
177
-
178
- declare global {
179
- namespace Cypress {
180
- interface Chainable {
181
- adjustViewport: () => Cypress.Chainable;
182
- cssDisableMotion: () => Cypress.Chainable;
183
- getRequestCalls: (alias) => Cypress.Chainable;
184
- interceptAPIRequest: <T extends object>(
185
- props: InterceptAPIRequestProps<T>
186
- ) => Cypress.Chainable;
187
- interceptRequest: (method, path, mock, alias) => Cypress.Chainable;
188
- makeSnapshot: (title?: string) => void;
189
- makeSnapshotWithCustomResolution: ({
190
- title,
191
- resolution
192
- }: MakeSnapshotWithCustomResolution) => Cypress.Chainable;
193
- mount: ({ Component, options }: MountProps) => Cypress.Chainable;
194
- moveSortableElement: ({
195
- element,
196
- direction,
197
- times
198
- }: MoveSortableElementProps) => void;
199
- moveSortableElementUsingAriaLabel: ({ ariaLabel, direction }) => void;
200
- waitForRequest: (alias) => Cypress.Chainable;
201
- }
202
- }
203
- }
1
+ /* eslint-disable @typescript-eslint/no-namespace */
2
+ import React from 'react';
3
+
4
+ import { mount } from 'cypress/react18';
5
+ import { equals, isNil } from 'ramda';
6
+
7
+ import { Box, CssBaseline } from '@mui/material';
8
+
9
+ import { ThemeProvider } from '@centreon/ui';
10
+
11
+ import '@testing-library/cypress/add-commands';
12
+ import 'cypress-msw-interceptor';
13
+ import 'cypress-real-events';
14
+
15
+ import disableMotion from './disableCssTransitions';
16
+
17
+ interface MountProps {
18
+ Component: React.ReactNode;
19
+ options?: object;
20
+ }
21
+ interface Resolution {
22
+ height: number;
23
+ width: number;
24
+ }
25
+
26
+ interface MakeSnapshotWithCustomResolution {
27
+ resolution: Resolution;
28
+ title: string;
29
+ }
30
+
31
+ export enum Method {
32
+ DELETE = 'DELETE',
33
+ GET = 'GET',
34
+ PATCH = 'PATCH',
35
+ POST = 'POST',
36
+ PUT = 'PUT'
37
+ }
38
+
39
+ Cypress.Commands.add('mount', ({ Component, options = {} }) => {
40
+ const wrapped = (
41
+ <ThemeProvider>
42
+ <Box
43
+ sx={{
44
+ backgroundColor: 'background.paper',
45
+ height: '100%',
46
+ width: '100%'
47
+ }}
48
+ >
49
+ {Component}
50
+ </Box>
51
+ <CssBaseline />
52
+ </ThemeProvider>
53
+ );
54
+
55
+ return mount(wrapped, options);
56
+ });
57
+
58
+ interface Query {
59
+ name: string;
60
+ value: string;
61
+ }
62
+
63
+ export interface InterceptAPIRequestProps<T> {
64
+ alias: string;
65
+ delay?: number;
66
+ method: Method;
67
+ path: string;
68
+ query?: Query;
69
+ response?: T | Array<T>;
70
+ statusCode?: number;
71
+ }
72
+
73
+ Cypress.Commands.add(
74
+ 'interceptAPIRequest',
75
+ <T extends object>({
76
+ method,
77
+ path,
78
+ response,
79
+ alias,
80
+ query,
81
+ statusCode = 200,
82
+ delay = 500
83
+ }: InterceptAPIRequestProps<T>): void => {
84
+ cy.interceptRequest(
85
+ method,
86
+ path.replace('./', '**'),
87
+ (req, res, ctx) => {
88
+ const getQuery = req?.url?.searchParams?.get(query?.name);
89
+ if (query && equals(query.value, getQuery)) {
90
+ return res(
91
+ ctx.delay(delay),
92
+ ctx.json(response),
93
+ ctx.status(statusCode)
94
+ );
95
+ }
96
+ if (!getQuery && isNil(query)) {
97
+ return res(
98
+ ctx.delay(delay),
99
+ ctx.json(response),
100
+ ctx.status(statusCode)
101
+ );
102
+ }
103
+
104
+ return null;
105
+ },
106
+ alias
107
+ );
108
+ }
109
+ );
110
+
111
+ interface MoveSortableElementProps {
112
+ direction: 'up' | 'down' | 'left' | 'right';
113
+ element: Cypress.Chainable<JQuery<HTMLElement>>;
114
+ times?: number;
115
+ }
116
+
117
+ Cypress.Commands.add(
118
+ 'moveSortableElement',
119
+ ({ element, direction, times = 1 }: MoveSortableElementProps): void => {
120
+ const key = `{${direction}arrow}`;
121
+
122
+ element.type(' ', {
123
+ force: true,
124
+ scrollBehavior: false
125
+ });
126
+
127
+ Array.from({ length: times }).forEach(() => {
128
+ element.eq(-1).type(key, {
129
+ scrollBehavior: false
130
+ });
131
+ });
132
+ element.eq(-1).type(' ', {
133
+ scrollBehavior: false
134
+ });
135
+ }
136
+ );
137
+
138
+ Cypress.Commands.add(
139
+ 'moveSortableElementUsingAriaLabel',
140
+ ({ ariaLabel, direction }): void => {
141
+ const key = `{${direction}arrow}`;
142
+
143
+ cy.findByLabelText(ariaLabel).type(' ', {
144
+ force: true,
145
+ scrollBehavior: false
146
+ });
147
+ cy.findAllByLabelText(ariaLabel).eq(-1).type(key, {
148
+ scrollBehavior: false
149
+ });
150
+ cy.findAllByLabelText(ariaLabel).eq(-1).type(' ', {
151
+ scrollBehavior: false
152
+ });
153
+ }
154
+ );
155
+
156
+ Cypress.Commands.add('adjustViewport', () => cy.viewport(1280, 590));
157
+
158
+ Cypress.Commands.add('makeSnapshot', (title?: string) => {
159
+ cy.adjustViewport();
160
+ cy.matchImageSnapshot(title);
161
+ });
162
+
163
+ Cypress.Commands.add(
164
+ 'makeSnapshotWithCustomResolution',
165
+ ({ title, resolution }: MakeSnapshotWithCustomResolution) => {
166
+ const { width, height } = resolution;
167
+ cy.viewport(width, height);
168
+ cy.matchImageSnapshot(title);
169
+ }
170
+ );
171
+
172
+ Cypress.Commands.add('cssDisableMotion', (): void => {
173
+ Cypress.on('window:before:load', (cyWindow) => {
174
+ disableMotion(cyWindow);
175
+ });
176
+ });
177
+
178
+ declare global {
179
+ namespace Cypress {
180
+ interface Chainable {
181
+ adjustViewport: () => Cypress.Chainable;
182
+ cssDisableMotion: () => Cypress.Chainable;
183
+ getRequestCalls: (alias) => Cypress.Chainable;
184
+ interceptAPIRequest: <T extends object>(
185
+ props: InterceptAPIRequestProps<T>
186
+ ) => Cypress.Chainable;
187
+ interceptRequest: (method, path, mock, alias) => Cypress.Chainable;
188
+ makeSnapshot: (title?: string) => void;
189
+ makeSnapshotWithCustomResolution: ({
190
+ title,
191
+ resolution
192
+ }: MakeSnapshotWithCustomResolution) => Cypress.Chainable;
193
+ mount: ({ Component, options }: MountProps) => Cypress.Chainable;
194
+ moveSortableElement: ({
195
+ element,
196
+ direction,
197
+ times
198
+ }: MoveSortableElementProps) => void;
199
+ moveSortableElementUsingAriaLabel: ({ ariaLabel, direction }) => void;
200
+ waitForRequest: (alias) => Cypress.Chainable;
201
+ }
202
+ }
203
+ }
@@ -1,69 +1,83 @@
1
- /* eslint-disable @typescript-eslint/no-var-requires */
2
- const { defineConfig } = require('cypress');
3
- const {
4
- addMatchImageSnapshotPlugin
5
- } = require('@simonsmith/cypress-image-snapshot/plugin');
6
- const cypressCodeCoverageTask = require('@cypress/code-coverage/task');
7
-
8
- module.exports = ({
9
- webpackConfig,
10
- cypressFolder,
11
- specPattern,
12
- env,
13
- useVite = false,
14
- excludeSpecPattern
15
- }) => {
16
- const mainCypressFolder = cypressFolder || 'cypress';
17
-
18
- return defineConfig({
19
- component: {
20
- devServer: {
21
- bundler: useVite ? 'vite' : 'webpack',
22
- framework: 'react',
23
- webpackConfig
24
- },
25
- excludeSpecPattern,
26
- setupNodeEvents: (on, config) => {
27
- addMatchImageSnapshotPlugin(on, config);
28
-
29
- cypressCodeCoverageTask(on, config);
30
-
31
- on('before:browser:launch', (browser, launchOptions) => {
32
- if (browser.name === 'chrome' && browser.isHeadless) {
33
- launchOptions.args.push('--headless=new');
34
- launchOptions.args.push('--force-color-profile=srgb');
35
- }
36
-
37
- return launchOptions;
38
- });
39
- },
40
- specPattern,
41
- supportFile: `${mainCypressFolder}/support/component.tsx`
42
- },
43
- env: {
44
- baseUrl: 'http://localhost:9092',
45
- codeCoverage: {
46
- exclude: [
47
- 'cypress/**/*.*',
48
- 'packages/**',
49
- 'node_modules',
50
- '**/*.js',
51
- '**/*.spec.tsx'
52
- ]
53
- },
54
- ...env
55
- },
56
- reporter: 'mochawesome',
57
- reporterOptions: {
58
- html: false,
59
- json: true,
60
- overwrite: true,
61
- reportDir: `${mainCypressFolder}/results`,
62
- reportFilename: '[name]-report.json'
63
- },
64
- video: true,
65
- videosFolder: `${mainCypressFolder}/results/videos`,
66
- viewportHeight: 590,
67
- viewportWidth: 1280
68
- });
69
- };
1
+ /* eslint-disable @typescript-eslint/no-var-requires */
2
+ const { defineConfig } = require('cypress');
3
+ const { devServer } = require('cypress-rspack-dev-server');
4
+ const {
5
+ addMatchImageSnapshotPlugin
6
+ } = require('@simonsmith/cypress-image-snapshot/plugin');
7
+ const cypressCodeCoverageTask = require('@cypress/code-coverage/task');
8
+
9
+ module.exports = ({
10
+ webpackConfig,
11
+ cypressFolder,
12
+ specPattern,
13
+ env,
14
+ useVite = false,
15
+ excludeSpecPattern
16
+ }) => {
17
+ const mainCypressFolder = cypressFolder || 'cypress';
18
+
19
+ return defineConfig({
20
+ component: {
21
+ devServer: useVite
22
+ ? {
23
+ bundler: 'vite',
24
+ framework: 'react',
25
+ webpackConfig
26
+ }
27
+ : (devServerConfig) =>
28
+ devServer({
29
+ ...devServerConfig,
30
+ framework: 'react',
31
+ rspackConfig: webpackConfig
32
+ }),
33
+ excludeSpecPattern,
34
+ setupNodeEvents: (on, config) => {
35
+ addMatchImageSnapshotPlugin(on, config);
36
+
37
+ cypressCodeCoverageTask(on, config);
38
+ on('task', {
39
+ coverageReport: () => {
40
+ return null;
41
+ }
42
+ });
43
+
44
+ on('before:browser:launch', (browser, launchOptions) => {
45
+ if (browser.name === 'chrome' && browser.isHeadless) {
46
+ launchOptions.args.push('--headless=new');
47
+ launchOptions.args.push('--force-color-profile=srgb');
48
+ launchOptions.args.push('--window-size=1400,1200');
49
+ }
50
+
51
+ return launchOptions;
52
+ });
53
+ },
54
+ specPattern,
55
+ supportFile: `${mainCypressFolder}/support/component.tsx`
56
+ },
57
+ env: {
58
+ baseUrl: 'http://localhost:9092',
59
+ codeCoverage: {
60
+ exclude: [
61
+ 'cypress/**/*.*',
62
+ 'packages/**',
63
+ 'node_modules',
64
+ '**/*.js',
65
+ '**/*.spec.tsx'
66
+ ]
67
+ },
68
+ ...env
69
+ },
70
+ reporter: 'mochawesome',
71
+ reporterOptions: {
72
+ html: false,
73
+ json: true,
74
+ overwrite: true,
75
+ reportDir: `${mainCypressFolder}/results`,
76
+ reportFilename: '[name]-report.json'
77
+ },
78
+ video: true,
79
+ videosFolder: `${mainCypressFolder}/results/videos`,
80
+ viewportHeight: 590,
81
+ viewportWidth: 1280
82
+ });
83
+ };
@@ -1,21 +1,21 @@
1
- import { addMatchImageSnapshotCommand } from '@simonsmith/cypress-image-snapshot/command';
2
-
3
- const enableVisualTesting = (cypressFolder = 'cypress'): void => {
4
- if (Cypress.config('isInteractive')) {
5
- Cypress.Commands.add('matchImageSnapshot', () => {
6
- cy.log('Skipping snapshot');
7
- });
8
-
9
- return;
10
- }
11
-
12
- addMatchImageSnapshotCommand({
13
- capture: 'viewport',
14
- customDiffConfig: { threshold: 0.01 },
15
- customSnapshotsDir: `${cypressFolder}/visual-testing-snapshots`,
16
- failureThreshold: 0.07,
17
- failureThresholdType: 'percent'
18
- });
19
- };
20
-
21
- export default enableVisualTesting;
1
+ import { addMatchImageSnapshotCommand } from '@simonsmith/cypress-image-snapshot/command';
2
+
3
+ const enableVisualTesting = (cypressFolder = 'cypress'): void => {
4
+ if (Cypress.config('isInteractive')) {
5
+ Cypress.Commands.add('matchImageSnapshot', () => {
6
+ cy.log('Skipping snapshot');
7
+ });
8
+
9
+ return;
10
+ }
11
+
12
+ addMatchImageSnapshotCommand({
13
+ capture: 'viewport',
14
+ customDiffConfig: { threshold: 0.01 },
15
+ customSnapshotsDir: `${cypressFolder}/visual-testing-snapshots`,
16
+ failureThreshold: 0.07,
17
+ failureThresholdType: 'percent'
18
+ });
19
+ };
20
+
21
+ export default enableVisualTesting;
@@ -29,6 +29,12 @@ interface SubmitResult {
29
29
  status: string;
30
30
  }
31
31
 
32
+ interface ServiceCheck {
33
+ host: string;
34
+ isForced?: boolean;
35
+ service: string;
36
+ }
37
+
32
38
  Cypress.Commands.add(
33
39
  'submitResults',
34
40
  (results: Array<SubmitResult>): Cypress.Chainable => {
@@ -64,10 +70,60 @@ Cypress.Commands.add(
64
70
  }
65
71
  );
66
72
 
73
+ Cypress.Commands.add(
74
+ 'scheduleServiceCheck',
75
+ ({
76
+ host,
77
+ isForced = true,
78
+ service
79
+ }: ServiceCheck): Cypress.Chainable => {
80
+ let query = `SELECT parent_id, id FROM resources WHERE parent_name = '${host}' AND name = '${service}'`;
81
+
82
+ return cy
83
+ .requestOnDatabase({
84
+ database: 'centreon_storage',
85
+ query
86
+ })
87
+ .then(([rows]) => {
88
+ if (rows.length === 0) {
89
+ throw new Error(`Cannot find service ${host} / ${service}`);
90
+ }
91
+
92
+ const hostId = rows[0].parent_id;
93
+ const serviceId = rows[0].id;
94
+
95
+ return cy.request({
96
+ body: {
97
+ check: {
98
+ is_forced: isForced
99
+ },
100
+ resources: [
101
+ {
102
+ id: serviceId,
103
+ parent: {
104
+ id: hostId
105
+ },
106
+ type: 'service'
107
+ }
108
+ ]
109
+ },
110
+ method: 'POST',
111
+ timeout: 30000,
112
+ url: '/centreon/api/latest/monitoring/resources/check'
113
+ }).then((response) => {
114
+ expect(response.status).to.eq(204);
115
+
116
+ return cy.wrap(null);
117
+ });
118
+ });
119
+ }
120
+ );
121
+
67
122
  declare global {
68
123
  namespace Cypress {
69
124
  interface Chainable {
70
125
  submitResults: (props: Array<SubmitResult>) => Cypress.Chainable;
126
+ scheduleServiceCheck: (serviceCheck) => Cypress.Chainable;
71
127
  }
72
128
  }
73
129
  }
@@ -79,6 +79,34 @@ Cypress.Commands.add('getIframeBody', (): Cypress.Chainable => {
79
79
  .then(cy.wrap);
80
80
  });
81
81
 
82
+ Cypress.Commands.add(
83
+ 'waitForElementInIframe',
84
+ (iframeSelector, elementSelector) => {
85
+ cy.waitUntil(
86
+ () =>
87
+ cy.get(iframeSelector).then(($iframe) => {
88
+ const iframeBody = $iframe[0].contentDocument.body;
89
+ if (iframeBody) {
90
+ const $element = Cypress.$(iframeBody).find(elementSelector);
91
+
92
+ return $element.length > 0 && $element.is(':visible');
93
+ }
94
+
95
+ return false;
96
+ }),
97
+ {
98
+ errorMsg: 'The element is not visible within the iframe',
99
+ interval: 5000,
100
+ timeout: 100000
101
+ }
102
+ ).then((isVisible) => {
103
+ if (!isVisible) {
104
+ throw new Error('The element is not visible');
105
+ }
106
+ });
107
+ }
108
+ );
109
+
82
110
  Cypress.Commands.add(
83
111
  'hoverRootMenuItem',
84
112
  (rootItemNumber: number): Cypress.Chainable => {
@@ -260,6 +288,7 @@ Cypress.Commands.add(
260
288
  return cy.get('.MuiAlert-message').then(($snackbar) => {
261
289
  if ($snackbar.text().includes('Login succeeded')) {
262
290
  cy.wait('@getNavigationList');
291
+ cy.get('.MuiAlert-message').should('not.be.visible');
263
292
  }
264
293
  });
265
294
  }
@@ -500,99 +529,111 @@ Cypress.Commands.add(
500
529
  }
501
530
  );
502
531
 
503
- Cypress.Commands.add('stopContainers', (): Cypress.Chainable => {
504
- cy.log('Stopping containers ...');
532
+ Cypress.Commands.add(
533
+ 'getLogDirectory',
534
+ (): Cypress.Chainable => {
535
+ const logDirectory = `results/logs/${Cypress.spec.name.replace(
536
+ artifactIllegalCharactersMatcher,
537
+ '_'
538
+ )}/${Cypress.currentTest.title.replace(
539
+ artifactIllegalCharactersMatcher,
540
+ '_'
541
+ )}`;
505
542
 
506
- const logDirectory = `results/logs/${Cypress.spec.name.replace(
507
- artifactIllegalCharactersMatcher,
508
- '_'
509
- )}/${Cypress.currentTest.title.replace(
510
- artifactIllegalCharactersMatcher,
511
- '_'
512
- )}`;
543
+ return cy
544
+ .createDirectory(logDirectory)
545
+ .exec(`chmod -R 755 "${logDirectory}"`)
546
+ .wrap(logDirectory);
547
+ }
548
+ );
513
549
 
514
- const name = 'web';
550
+ interface CopyWebContainerLogsProps {
551
+ name: string;
552
+ }
515
553
 
516
- return cy
517
- .visitEmptyPage()
518
- .createDirectory(logDirectory)
519
- .getContainersLogs()
520
- .then((containersLogs: Array<Array<string>>) => {
521
- Object.entries(containersLogs).forEach(([containerName, logs]) => {
522
- cy.writeFile(
523
- `results/logs/${Cypress.spec.name.replace(
524
- artifactIllegalCharactersMatcher,
525
- '_'
526
- )}/${Cypress.currentTest.title.replace(
527
- artifactIllegalCharactersMatcher,
528
- '_'
529
- )}/container-${containerName}.log`,
530
- logs
531
- );
532
- });
533
- })
534
- .copyFromContainer({
535
- destination: `${logDirectory}/broker`,
536
- name,
537
- source: '/var/log/centreon-broker'
538
- })
539
- .copyFromContainer({
540
- destination: `${logDirectory}/engine`,
541
- name,
542
- source: '/var/log/centreon-engine'
543
- })
544
- .copyFromContainer({
545
- destination: `${logDirectory}/centreon`,
546
- name,
547
- source: '/var/log/centreon'
548
- })
549
- .copyFromContainer({
550
- destination: `${logDirectory}/centreon-gorgone`,
551
- name,
552
- source: '/var/log/centreon-gorgone'
553
- })
554
- .then(() => {
554
+ Cypress.Commands.add(
555
+ 'copyWebContainerLogs',
556
+ ({ name }: CopyWebContainerLogsProps): Cypress.Chainable => {
557
+ cy.log(`Getting logs from container ${name} ...`);
558
+
559
+ return cy.getLogDirectory().then((logDirectory) => {
560
+ let sourcePhpLogs = '/var/log/php8.1-fpm-centreon-error.log';
561
+ let targetPhpLogs = `${logDirectory}/php8.1-fpm-centreon-error.log`;
562
+ let sourceApacheLogs = '/var/log/apache2';
563
+ let targetApacheLogs = `${logDirectory}/apache2`;
555
564
  if (Cypress.env('WEB_IMAGE_OS').includes('alma')) {
556
- return cy.copyFromContainer({
557
- destination: `${logDirectory}/php`,
558
- name,
559
- source: '/var/log/php-fpm'
560
- });
565
+ sourcePhpLogs = '/var/log/php-fpm';
566
+ targetPhpLogs = `${logDirectory}/php`;
567
+ sourceApacheLogs = '/var/log/httpd';
568
+ targetApacheLogs = `${logDirectory}/httpd`;
561
569
  }
562
570
 
563
- return cy.copyFromContainer(
564
- {
565
- destination: `${logDirectory}/php8.1-fpm-centreon-error.log`,
571
+ return cy
572
+ .copyFromContainer({
573
+ destination: `${logDirectory}/broker`,
566
574
  name,
567
- source: '/var/log/php8.1-fpm-centreon-error.log'
568
- },
569
- { failOnNonZeroExit: false }
570
- );
571
- })
572
- .then(() => {
573
- if (Cypress.env('WEB_IMAGE_OS').includes('alma')) {
574
- return cy.copyFromContainer({
575
- destination: `${logDirectory}/httpd`,
575
+ source: '/var/log/centreon-broker'
576
+ })
577
+ .copyFromContainer({
578
+ destination: `${logDirectory}/engine`,
576
579
  name,
577
- source: '/var/log/httpd'
578
- });
579
- }
580
-
581
- return cy.copyFromContainer(
582
- {
583
- destination: `${logDirectory}/apache2`,
580
+ source: '/var/log/centreon-engine'
581
+ })
582
+ .copyFromContainer({
583
+ destination: `${logDirectory}/centreon`,
584
584
  name,
585
- source: '/var/log/apache2'
586
- },
587
- { failOnNonZeroExit: false }
588
- );
589
- })
590
- .exec(`chmod -R 755 "${logDirectory}"`)
591
- .task(
592
- 'stopContainers',
593
- {},
594
- { timeout: 600000 } // 10 minutes because docker pull can be very slow
595
- );
585
+ source: '/var/log/centreon'
586
+ })
587
+ .copyFromContainer({
588
+ destination: `${logDirectory}/centreon-gorgone`,
589
+ name,
590
+ source: '/var/log/centreon-gorgone'
591
+ })
592
+ .copyFromContainer({
593
+ destination: targetPhpLogs,
594
+ name,
595
+ source: sourcePhpLogs,
596
+ })
597
+ .copyFromContainer({
598
+ destination: targetApacheLogs,
599
+ name,
600
+ source: sourceApacheLogs,
601
+ })
602
+ .exec(`chmod -R 755 "${logDirectory}"`);
603
+ });
604
+ });
605
+
606
+ Cypress.Commands.add('stopContainers', (): Cypress.Chainable => {
607
+ cy.log('Stopping containers ...');
608
+
609
+ const name = 'web';
610
+
611
+ return cy
612
+ .visitEmptyPage()
613
+ .getLogDirectory()
614
+ .then((logDirectory) => {
615
+ return cy
616
+ .getContainersLogs()
617
+ .then((containersLogs: Array<Array<string>>) => {
618
+ if (!containersLogs) {
619
+ return;
620
+ }
621
+
622
+ Object.entries(containersLogs).forEach(([containerName, logs]) => {
623
+ cy.writeFile(
624
+ `${logDirectory}/container-${containerName}.log`,
625
+ logs
626
+ );
627
+ });
628
+ })
629
+ .copyWebContainerLogs({ name })
630
+ .exec(`chmod -R 755 "${logDirectory}"`)
631
+ .task(
632
+ 'stopContainers',
633
+ {},
634
+ { timeout: 600000 } // 10 minutes because docker pull can be very slow
635
+ );
636
+ });
596
637
  });
597
638
 
598
639
  Cypress.Commands.add(
@@ -743,7 +784,7 @@ Cypress.Commands.add(
743
784
 
744
785
  Cypress.Commands.add('getTimeFromHeader', (): Cypress.Chainable => {
745
786
  return cy
746
- .get('header div[data-cy="clock"]', { timeout: 10000 })
787
+ .get('header div[data-cy="clock"]', { timeout: 20000 })
747
788
  .should('be.visible')
748
789
  .then(($time) => {
749
790
  const headerTime = $time.children()[1].textContent;
@@ -769,6 +810,7 @@ declare global {
769
810
  props: CopyToContainerProps,
770
811
  options?: Partial<Cypress.ExecOptions>
771
812
  ) => Cypress.Chainable;
813
+ copyWebContainerLogs: (props: CopyWebContainerLogsProps) => Cypress.Chainable;
772
814
  createDirectory: (directoryPath: string) => Cypress.Chainable;
773
815
  execInContainer: ({
774
816
  command,
@@ -788,6 +830,7 @@ declare global {
788
830
  getContainerIpAddress: (containerName: string) => Cypress.Chainable;
789
831
  getContainersLogs: () => Cypress.Chainable;
790
832
  getIframeBody: () => Cypress.Chainable;
833
+ getLogDirectory: () => Cypress.Chainable;
791
834
  getTimeFromHeader: () => Cypress.Chainable;
792
835
  getWebVersion: () => Cypress.Chainable;
793
836
  hoverRootMenuItem: (rootItemNumber: number) => Cypress.Chainable;
@@ -839,6 +882,10 @@ declare global {
839
882
  webOs,
840
883
  webVersion
841
884
  }?: StartContainersProps) => Cypress.Chainable;
885
+ waitForElementInIframe: (
886
+ iframeSelector: string,
887
+ elementSelector: string
888
+ ) => Cypress.Chainable;
842
889
  stopContainer: ({ name }: StopContainerProps) => Cypress.Chainable;
843
890
  stopContainers: () => Cypress.Chainable;
844
891
  visitEmptyPage: () => Cypress.Chainable;
@@ -38,7 +38,7 @@ export default ({
38
38
 
39
39
  return defineConfig({
40
40
  chromeWebSecurity: false,
41
- defaultCommandTimeout: 6000,
41
+ defaultCommandTimeout: 20000,
42
42
  downloadsFolder: `${resultsFolder}/downloads`,
43
43
  e2e: {
44
44
  excludeSpecPattern: ['*.js', '*.ts', '*.md'],
@@ -47,15 +47,26 @@ export default ({
47
47
  reporterOptions: {
48
48
  configFile: `${__dirname}/reporter-config.js`
49
49
  },
50
- setupNodeEvents: async (on, config) => {
51
- 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
+ });
52
62
  await esbuildPreprocessor(on, config);
53
63
  tasks(on);
54
64
 
55
65
  return plugins(on, config);
56
66
  },
57
67
  specPattern,
58
- supportFile: 'support/e2e.{js,jsx,ts,tsx}'
68
+ supportFile: 'support/e2e.{js,jsx,ts,tsx}',
69
+ testIsolation: true,
59
70
  },
60
71
  env: {
61
72
  ...env,
@@ -66,11 +77,16 @@ export default ({
66
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
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
@@ -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
+ };
@@ -55,19 +55,17 @@ export default (on: Cypress.PluginEvents): void => {
55
55
  on('task', {
56
56
  copyFromContainer: async ({ destination, serviceName, source }) => {
57
57
  try {
58
- if (dockerEnvironment !== null) {
59
- const container = dockerEnvironment.getContainer(`${serviceName}-1`);
60
-
61
- await container
62
- .copyArchiveFromContainer(source)
63
- .then((archiveStream) => {
64
- return new Promise<void>((resolve) => {
65
- const dest = tar.extract(destination);
66
- archiveStream.pipe(dest);
67
- dest.on('finish', resolve);
68
- });
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);
69
67
  });
70
- }
68
+ });
71
69
  } catch (error) {
72
70
  console.error(error);
73
71
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@centreon/js-config",
3
3
  "description": "Centreon Frontend shared build configuration",
4
- "version": "24.4.32",
4
+ "version": "24.4.34",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/centreon/centreon-frontend.git"
@@ -1,20 +1,46 @@
1
+ const fs = require('fs');
1
2
  const { CleanWebpackPlugin } = require('clean-webpack-plugin');
2
3
 
3
- const WriteRemoteEntryNameToModuleFederation = require('../plugins/WriteRemoteEntryNameToModuleFederation');
4
- const TransformPreloadScript = require('../plugins/TransformPreloadScript');
4
+ class CentreonModulePlugin {
5
+ constructor(federatedComponentConfiguration) {
6
+ this.federatedComponentConfiguration = federatedComponentConfiguration;
7
+ }
5
8
 
6
- module.exports = ({ outputPath, federatedComponentConfiguration }) => ({
9
+ apply(compiler) {
10
+ compiler.hooks.done.tap('CentreonModulePlugin', (stats) => {
11
+ const newFederatedComponentConfiguration = {
12
+ ...this.federatedComponentConfiguration,
13
+ remoteEntry: Object.keys(stats.compilation.assets).find((assetName) =>
14
+ assetName.match(/(^remoteEntry)\S+.js$/),
15
+ ),
16
+ };
17
+
18
+ if (!fs.existsSync(compiler.options.output.path)) {
19
+ fs.mkdirSync(compiler.options.output.path, { recursive: true });
20
+ }
21
+
22
+ fs.writeFileSync(
23
+ `${compiler.options.output.path}/moduleFederation.json`,
24
+ JSON.stringify(newFederatedComponentConfiguration, null, 2),
25
+ );
26
+ });
27
+ }
28
+ }
29
+
30
+ module.exports = ({
31
+ outputPath,
32
+ federatedComponentConfiguration,
33
+ }) => ({
7
34
  output: {
8
35
  library: '[chunkhash:8]',
9
- path: outputPath
36
+ path: outputPath,
10
37
  },
11
38
  plugins: [
12
39
  new CleanWebpackPlugin({
13
40
  cleanOnceBeforeBuildPatterns: [`${outputPath}/**/*.js`],
14
41
  dangerouslyAllowCleanPatternsOutsideProject: true,
15
- dry: false
42
+ dry: false,
16
43
  }),
17
- new WriteRemoteEntryNameToModuleFederation(federatedComponentConfiguration),
18
- new TransformPreloadScript(federatedComponentConfiguration)
19
- ]
44
+ new CentreonModulePlugin(federatedComponentConfiguration),
45
+ ],
20
46
  });
@@ -1,37 +0,0 @@
1
- const fs = require('fs');
2
-
3
- const swc = require('@swc/core');
4
-
5
- module.exports = class TransformPreloadScript {
6
- constructor(federatedComponentConfiguration) {
7
- this.federatedComponentConfiguration = federatedComponentConfiguration;
8
- }
9
-
10
- apply(compiler) {
11
- compiler.hooks.done.tap('CentreonModulePlugin', () => {
12
- if (!fs.existsSync(compiler.options.output.path)) {
13
- fs.mkdirSync(compiler.options.output.path, { recursive: true });
14
- }
15
-
16
- if (this.federatedComponentConfiguration.preloadScript) {
17
- const { code } = swc.transformFileSync(
18
- `./${this.federatedComponentConfiguration.preloadScript}.ts`,
19
- {
20
- filename: `${this.federatedComponentConfiguration.preloadScript}.ts`,
21
- jsc: {
22
- parser: {
23
- syntax: 'typescript'
24
- }
25
- },
26
- minify: true
27
- }
28
- );
29
-
30
- fs.writeFileSync(
31
- `${compiler.options.output.path}/${this.federatedComponentConfiguration.preloadScript}.js`,
32
- code
33
- );
34
- }
35
- });
36
- }
37
- };
@@ -1,30 +0,0 @@
1
- const fs = require('fs');
2
-
3
- module.exports = class WriteRemoteEntryNameToModuleFederation {
4
- constructor(federatedComponentConfiguration) {
5
- this.federatedComponentConfiguration = federatedComponentConfiguration;
6
- }
7
-
8
- apply(compiler) {
9
- compiler.hooks.done.tap(
10
- 'WriteRemoteEntryNameToModuleFederation',
11
- (stats) => {
12
- const newFederatedComponentConfiguration = {
13
- ...this.federatedComponentConfiguration,
14
- remoteEntry: Object.keys(stats.compilation.assets).find((assetName) =>
15
- assetName.match(/(^remoteEntry)\S+.js$/)
16
- )
17
- };
18
-
19
- if (!fs.existsSync(compiler.options.output.path)) {
20
- fs.mkdirSync(compiler.options.output.path, { recursive: true });
21
- }
22
-
23
- fs.writeFileSync(
24
- `${compiler.options.output.path}/moduleFederation.json`,
25
- JSON.stringify(newFederatedComponentConfiguration, null, 2)
26
- );
27
- }
28
- );
29
- }
30
- };