@centreon/js-config 23.10.46 → 23.10.47

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.
@@ -4,7 +4,7 @@ import React from 'react';
4
4
  import { mount } from 'cypress/react18';
5
5
  import { equals, isNil } from 'ramda';
6
6
 
7
- import { Box, CssBaseline } from '@mui/material';
7
+ import { Box } from '@mui/material';
8
8
 
9
9
  import { ThemeProvider } from '@centreon/ui';
10
10
 
@@ -24,7 +24,7 @@ export enum Method {
24
24
  PUT = 'PUT'
25
25
  }
26
26
 
27
- Cypress.Commands.add('mount', ({ Component, options = {} }) => {
27
+ Cypress.Commands.add('mount', ({ Component, options }) => {
28
28
  const wrapped = (
29
29
  <ThemeProvider>
30
30
  <Box
@@ -36,10 +36,11 @@ Cypress.Commands.add('mount', ({ Component, options = {} }) => {
36
36
  >
37
37
  {Component}
38
38
  </Box>
39
- <CssBaseline />
40
39
  </ThemeProvider>
41
40
  );
42
41
 
42
+ document.getElementsByTagName('body')[0].setAttribute('style', 'margin:0px');
43
+
43
44
  return mount(wrapped, options);
44
45
  });
45
46
 
@@ -127,11 +128,6 @@ Cypress.Commands.add(
127
128
  }
128
129
  );
129
130
 
130
- Cypress.Commands.add('makeSnapshot', (title?: string) => {
131
- cy.viewport(1280, 590);
132
- cy.matchImageSnapshot(title);
133
- });
134
-
135
131
  declare global {
136
132
  namespace Cypress {
137
133
  interface Chainable {
@@ -139,9 +135,8 @@ declare global {
139
135
  props: InterceptAPIRequestProps<T>
140
136
  ) => Cypress.Chainable;
141
137
  interceptRequest: (method, path, mock, alias) => Cypress.Chainable;
142
- makeSnapshot: (title?: string) => void;
143
- mount: ({ Component, options }: MountProps) => Cypress.Chainable;
144
- moveSortableElement: ({ element, direction }) => void;
138
+ mount: ({ Component, options = {} }: MountProps) => Cypress.Chainable;
139
+ moveSortableElement: ({ ariaLabel, direction }) => void;
145
140
  moveSortableElementUsingAriaLabel: ({ ariaLabel, direction }) => void;
146
141
  waitForRequest: (alias) => Cypress.Chainable;
147
142
  }
@@ -4,30 +4,28 @@ const {
4
4
  addMatchImageSnapshotPlugin
5
5
  } = require('@simonsmith/cypress-image-snapshot/plugin');
6
6
 
7
- module.exports = ({
8
- webpackConfig,
9
- cypressFolder,
10
- specPattern,
11
- env,
12
- useVite = false,
13
- excludeSpecPattern
14
- }) => {
7
+ module.exports = ({ webpackConfig, cypressFolder, specPattern, env }) => {
15
8
  const mainCypressFolder = cypressFolder || 'cypress';
16
9
 
17
10
  return defineConfig({
18
11
  component: {
19
12
  devServer: {
20
- bundler: useVite ? 'vite' : 'webpack',
13
+ bundler: 'webpack',
21
14
  framework: 'react',
22
15
  webpackConfig
23
16
  },
24
- excludeSpecPattern,
25
17
  setupNodeEvents: (on, config) => {
26
18
  addMatchImageSnapshotPlugin(on, config);
27
19
 
28
20
  on('before:browser:launch', (browser, launchOptions) => {
29
21
  if (browser.name === 'chrome' && browser.isHeadless) {
30
- launchOptions.args.push('--headless=new');
22
+ launchOptions.args = launchOptions.args.map((arg) => {
23
+ if (arg === '--headless') {
24
+ return '--headless=new';
25
+ }
26
+
27
+ return arg;
28
+ });
31
29
  }
32
30
 
33
31
  return launchOptions;
@@ -13,7 +13,7 @@ const enableVisualTesting = (cypressFolder = 'cypress'): void => {
13
13
  capture: 'viewport',
14
14
  customDiffConfig: { threshold: 0.01 },
15
15
  customSnapshotsDir: `${cypressFolder}/visual-testing-snapshots`,
16
- failureThreshold: 0.07,
16
+ failureThreshold: 0.06,
17
17
  failureThresholdType: 'percent'
18
18
  });
19
19
  };
@@ -1,11 +1,6 @@
1
1
  /* eslint-disable @typescript-eslint/no-namespace */
2
2
 
3
3
  import './commands/configuration';
4
- import './commands/monitoring';
5
-
6
- import installLogsCollector from 'cypress-terminal-report/src/installLogsCollector';
7
-
8
- installLogsCollector();
9
4
 
10
5
  const apiLoginV2 = '/centreon/authentication/providers/configurations/local';
11
6
 
@@ -28,7 +23,7 @@ Cypress.Commands.add('getWebVersion', (): Cypress.Chainable => {
28
23
 
29
24
  Cypress.Commands.add('getIframeBody', (): Cypress.Chainable => {
30
25
  return cy
31
- .get('iframe#main-content', { timeout: 10000 })
26
+ .get('iframe#main-content')
32
27
  .its('0.contentDocument.body')
33
28
  .should('not.be.empty')
34
29
  .then(cy.wrap);
@@ -44,13 +39,6 @@ Cypress.Commands.add(
44
39
  }
45
40
  );
46
41
 
47
- Cypress.Commands.add(
48
- 'clickSubRootMenuItem',
49
- (page: string): Cypress.Chainable => {
50
- return cy.get('div[data-cy="collapse"]').eq(1).contains(page).click();
51
- }
52
- );
53
-
54
42
  interface NavigateToProps {
55
43
  page: string;
56
44
  rootItemNumber: number;
@@ -63,13 +51,8 @@ Cypress.Commands.add(
63
51
  if (subMenu) {
64
52
  cy.hoverRootMenuItem(rootItemNumber)
65
53
  .contains(subMenu)
66
- .trigger('mouseover')
67
- .get('.MuiCollapse-wrapper')
68
- .find('div[data-cy="collapse"]')
69
- .should('be.visible')
70
- .and('contain', page);
71
-
72
- cy.clickSubRootMenuItem(page);
54
+ .trigger('mouseover', { force: true });
55
+ cy.contains(page).click({ force: true });
73
56
 
74
57
  return;
75
58
  }
@@ -102,41 +85,29 @@ Cypress.Commands.add(
102
85
 
103
86
  interface CopyFromContainerProps {
104
87
  destination: string;
105
- name?: string;
106
88
  source: string;
107
89
  }
108
90
 
109
91
  Cypress.Commands.add(
110
92
  'copyFromContainer',
111
- (
112
- {
113
- name = Cypress.env('dockerName'),
114
- source,
115
- destination
116
- }: CopyFromContainerProps,
117
- options?: Partial<Cypress.ExecOptions>
118
- ) => {
119
- return cy.exec(`docker cp ${name}:${source} "${destination}"`, options);
93
+ ({ source, destination }: CopyFromContainerProps) => {
94
+ return cy.exec(
95
+ `docker cp ${Cypress.env('dockerName')}:${source} "${destination}"`
96
+ );
120
97
  }
121
98
  );
122
99
 
123
100
  interface CopyToContainerProps {
124
101
  destination: string;
125
- name?: string;
126
102
  source: string;
127
103
  }
128
104
 
129
105
  Cypress.Commands.add(
130
106
  'copyToContainer',
131
- (
132
- {
133
- name = Cypress.env('dockerName'),
134
- source,
135
- destination
136
- }: CopyToContainerProps,
137
- options?: Partial<Cypress.ExecOptions>
138
- ) => {
139
- return cy.exec(`docker cp ${source} ${name}:${destination}`, options);
107
+ ({ source, destination }: CopyToContainerProps) => {
108
+ return cy.exec(
109
+ `docker cp ${source} ${Cypress.env('dockerName')}:${destination}`
110
+ );
140
111
  }
141
112
  );
142
113
 
@@ -147,7 +118,7 @@ interface LoginByTypeOfUserProps {
147
118
 
148
119
  Cypress.Commands.add(
149
120
  'loginByTypeOfUser',
150
- ({ jsonName = 'admin', loginViaApi = false }): Cypress.Chainable => {
121
+ ({ jsonName, loginViaApi }): Cypress.Chainable => {
151
122
  if (loginViaApi) {
152
123
  return cy
153
124
  .fixture(`users/${jsonName}.json`)
@@ -164,15 +135,12 @@ Cypress.Commands.add(
164
135
  .visit(`${Cypress.config().baseUrl}`)
165
136
  .wait('@getNavigationList');
166
137
  }
167
-
168
138
  cy.visit(`${Cypress.config().baseUrl}`)
169
139
  .fixture(`users/${jsonName}.json`)
170
140
  .then((credential) => {
171
- cy.getByLabel({ label: 'Alias', tag: 'input' }).type(
172
- `{selectAll}{backspace}${credential.login}`
173
- );
141
+ cy.getByLabel({ label: 'Alias', tag: 'input' }).type(credential.login);
174
142
  cy.getByLabel({ label: 'Password', tag: 'input' }).type(
175
- `{selectAll}{backspace}${credential.password}`
143
+ credential.password
176
144
  );
177
145
  })
178
146
  .getByLabel({ label: 'Connect', tag: 'button' })
@@ -242,18 +210,9 @@ interface StartContainerProps {
242
210
  Cypress.Commands.add(
243
211
  'startContainer',
244
212
  ({ name, image, portBindings }: StartContainerProps): Cypress.Chainable => {
245
- return cy.task(
246
- 'startContainer',
247
- { image, name, portBindings },
248
- { timeout: 600000 } // 10 minutes because docker pull can be very slow
249
- );
250
- }
251
- );
252
-
253
- Cypress.Commands.add(
254
- 'createDirectory',
255
- (directoryPath: string): Cypress.Chainable => {
256
- return cy.task('createDirectory', directoryPath);
213
+ return cy
214
+ .exec(`docker image inspect ${image} || docker pull ${image}`)
215
+ .task('startContainer', { image, name, portBindings });
257
216
  }
258
217
  );
259
218
 
@@ -268,7 +227,7 @@ Cypress.Commands.add(
268
227
  'startWebContainer',
269
228
  ({
270
229
  name = Cypress.env('dockerName'),
271
- os = Cypress.env('WEB_IMAGE_OS'),
230
+ os = 'alma9',
272
231
  useSlim = true,
273
232
  version = Cypress.env('WEB_IMAGE_VERSION')
274
233
  }: StartWebContainerProps = {}): Cypress.Chainable => {
@@ -283,13 +242,12 @@ Cypress.Commands.add(
283
242
  portBindings: [{ destination: 4000, source: 80 }]
284
243
  })
285
244
  .then(() => {
286
- const baseUrl = 'http://127.0.0.1:4000';
245
+ const baseUrl = 'http://0.0.0.0:4000';
287
246
 
288
247
  Cypress.config('baseUrl', baseUrl);
289
248
 
290
- return cy.task(
291
- 'waitOn',
292
- `${baseUrl}/centreon/api/latest/platform/installation/status`
249
+ return cy.exec(
250
+ `npx wait-on ${baseUrl}/centreon/api/latest/platform/installation/status`
293
251
  );
294
252
  })
295
253
  .visit('/') // this is necessary to refresh browser cause baseUrl has changed (flash appears in video)
@@ -316,41 +274,25 @@ Cypress.Commands.add(
316
274
 
317
275
  return cy
318
276
  .visitEmptyPage()
319
- .createDirectory(logDirectory)
277
+ .exec(`mkdir -p "${logDirectory}"`)
320
278
  .copyFromContainer({
321
279
  destination: `${logDirectory}/broker`,
322
- name,
323
280
  source: '/var/log/centreon-broker'
324
281
  })
325
282
  .copyFromContainer({
326
283
  destination: `${logDirectory}/engine`,
327
- name,
328
284
  source: '/var/log/centreon-engine'
329
285
  })
286
+ .execInContainer({
287
+ command: `bash -e <<EOF
288
+ chmod 777 /var/log/centreon/centreon-web.log > /dev/null 2>&1 || :
289
+ EOF`,
290
+ name
291
+ })
330
292
  .copyFromContainer({
331
293
  destination: `${logDirectory}/centreon`,
332
- name,
333
294
  source: '/var/log/centreon'
334
295
  })
335
- .then(() => {
336
- if (Cypress.env('WEB_IMAGE_OS').includes('alma')) {
337
- return cy.copyFromContainer({
338
- destination: `${logDirectory}/php`,
339
- name,
340
- source: '/var/log/php-fpm'
341
- });
342
- }
343
-
344
- return cy.copyFromContainer(
345
- {
346
- destination: `${logDirectory}/php8.1-fpm-centreon-error.log`,
347
- name,
348
- source: '/var/log/php8.1-fpm-centreon-error.log'
349
- },
350
- { failOnNonZeroExit: false }
351
- );
352
- })
353
- .exec(`chmod -R 755 "${logDirectory}"`)
354
296
  .stopContainer({ name });
355
297
  }
356
298
  );
@@ -379,128 +321,21 @@ Cypress.Commands.add(
379
321
  }
380
322
  );
381
323
 
382
- interface Dashboard {
383
- description?: string;
384
- name: string;
385
- }
386
-
387
- Cypress.Commands.add(
388
- 'insertDashboardList',
389
- (fixtureFile: string): Cypress.Chainable => {
390
- return cy.fixture(fixtureFile).then((dashboardList) => {
391
- cy.wrap(
392
- Promise.all(
393
- dashboardList.map((dashboardBody: Dashboard) =>
394
- cy.insertDashboard({ ...dashboardBody })
395
- )
396
- )
397
- );
398
- });
399
- }
400
- );
401
-
402
- Cypress.Commands.add(
403
- 'insertDashboard',
404
- (dashboardBody: Dashboard): Cypress.Chainable => {
405
- return cy.request({
406
- body: {
407
- ...dashboardBody
408
- },
409
- method: 'POST',
410
- url: '/centreon/api/latest/configuration/dashboards'
411
- });
412
- }
413
- );
414
-
415
- interface ShareDashboardToUserProps {
416
- dashboardName: string;
417
- role: string;
418
- userName: string;
419
- }
420
-
421
- interface ListingRequestResult {
422
- body: {
423
- result: Array<{
424
- id: number;
425
- }>;
426
- };
427
- }
428
-
429
- Cypress.Commands.add(
430
- 'shareDashboardToUser',
431
- ({ dashboardName, userName, role }: ShareDashboardToUserProps): void => {
432
- Promise.all([
433
- cy.request({
434
- method: 'GET',
435
- url: `/centreon/api/latest/configuration/users?search={"name":"${userName}"}`
436
- }),
437
- cy.request({
438
- method: 'GET',
439
- url: `/centreon/api/latest/configuration/dashboards?search={"name":"${dashboardName}"}`
440
- })
441
- ]).then(
442
- ([retrievedUser, retrievedDashboard]: [
443
- ListingRequestResult,
444
- ListingRequestResult
445
- ]) => {
446
- const userId = retrievedUser.body.result[0].id;
447
- const dashboardId = retrievedDashboard.body.result[0].id;
448
-
449
- cy.request({
450
- body: {
451
- id: userId,
452
- role: `${role}`
453
- },
454
- method: 'POST',
455
- url: `/centreon/api/latest/configuration/dashboards/${dashboardId}/access_rights/contacts`
456
- });
457
- }
458
- );
459
- }
460
- );
461
-
462
- Cypress.Commands.add('getTimeFromHeader', (): Cypress.Chainable => {
463
- return cy
464
- .get('header div[data-cy="clock"]', { timeout: 10000 })
465
- .should('be.visible')
466
- .then(($time) => {
467
- const headerTime = $time.children()[1].textContent;
468
- if (headerTime?.match(/\d+:\d+/)) {
469
- cy.log(`header time is : ${headerTime}`);
470
-
471
- return cy.wrap(headerTime);
472
- }
473
-
474
- throw new Error(`header time is not displayed`);
475
- });
476
- });
477
-
478
324
  declare global {
479
325
  namespace Cypress {
480
326
  interface Chainable {
481
- clickSubRootMenuItem: (page: string) => Cypress.Chainable;
482
- copyFromContainer: (
483
- props: CopyFromContainerProps,
484
- options?: Partial<Cypress.ExecOptions>
485
- ) => Cypress.Chainable;
486
- copyToContainer: (
487
- props: CopyToContainerProps,
488
- options?: Partial<Cypress.ExecOptions>
489
- ) => Cypress.Chainable;
490
- createDirectory: (directoryPath: string) => Cypress.Chainable;
327
+ copyFromContainer: (props: CopyFromContainerProps) => Cypress.Chainable;
328
+ copyToContainer: (props: CopyToContainerProps) => Cypress.Chainable;
491
329
  execInContainer: ({
492
330
  command,
493
331
  name
494
332
  }: ExecInContainerProps) => Cypress.Chainable;
495
333
  getIframeBody: () => Cypress.Chainable;
496
- getTimeFromHeader: () => Cypress.Chainable;
497
334
  getWebVersion: () => Cypress.Chainable;
498
335
  hoverRootMenuItem: (rootItemNumber: number) => Cypress.Chainable;
499
- insertDashboard: (dashboard: Dashboard) => Cypress.Chainable;
500
- insertDashboardList: (fixtureFile: string) => Cypress.Chainable;
501
336
  loginByTypeOfUser: ({
502
- jsonName,
503
- loginViaApi
337
+ jsonName = 'admin',
338
+ loginViaApi = false
504
339
  }: LoginByTypeOfUserProps) => Cypress.Chainable;
505
340
  moveSortableElement: (direction: string) => Cypress.Chainable;
506
341
  navigateTo: ({
@@ -508,11 +343,6 @@ declare global {
508
343
  rootItemNumber,
509
344
  subMenu
510
345
  }: NavigateToProps) => Cypress.Chainable;
511
- shareDashboardToUser: ({
512
- dashboardName,
513
- userName,
514
- role
515
- }: ShareDashboardToUserProps) => Cypress.Chainable;
516
346
  startContainer: ({
517
347
  name,
518
348
  image
@@ -4,11 +4,8 @@
4
4
  import { execSync } from 'child_process';
5
5
 
6
6
  import { defineConfig } from 'cypress';
7
- import installLogsPrinter from 'cypress-terminal-report/src/installLogsPrinter';
8
7
 
9
- import esbuildPreprocessor from './esbuild-preprocessor';
10
- import plugins from './plugins';
11
- import tasks from './tasks';
8
+ import setupNodeEvents from './plugins';
12
9
 
13
10
  interface ConfigurationOptions {
14
11
  cypressFolder?: string;
@@ -25,7 +22,9 @@ export default ({
25
22
  dockerName,
26
23
  env
27
24
  }: ConfigurationOptions): Cypress.ConfigOptions => {
28
- const resultsFolder = `${cypressFolder || '.'}/results`;
25
+ const resultsFolder = `${cypressFolder || 'cypress'}/results${
26
+ isDevelopment ? '/dev' : ''
27
+ }`;
29
28
 
30
29
  const webImageVersion = execSync('git rev-parse --abbrev-ref HEAD')
31
30
  .toString('utf8')
@@ -36,20 +35,8 @@ export default ({
36
35
  defaultCommandTimeout: 6000,
37
36
  e2e: {
38
37
  excludeSpecPattern: ['*.js', '*.ts', '*.md'],
39
- fixturesFolder: 'fixtures',
40
- reporter: require.resolve('cypress-multi-reporters'),
41
- reporterOptions: {
42
- configFile: `${__dirname}/reporter-config.js`
43
- },
44
- setupNodeEvents: async (on, config) => {
45
- installLogsPrinter(on);
46
- await esbuildPreprocessor(on, config);
47
- tasks(on);
48
-
49
- return plugins(on, config);
50
- },
51
- specPattern,
52
- supportFile: 'support/e2e.{js,jsx,ts,tsx}'
38
+ setupNodeEvents,
39
+ specPattern
53
40
  },
54
41
  env: {
55
42
  ...env,
@@ -58,12 +45,34 @@ export default ({
58
45
  WEB_IMAGE_VERSION: webImageVersion,
59
46
  dockerName: dockerName || 'centreon-dev'
60
47
  },
61
- execTimeout: 60000,
48
+ execTimeout: 120000,
49
+ reporter: 'mochawesome',
50
+ reporterOptions: {
51
+ html: false,
52
+ json: true,
53
+ overwrite: true,
54
+ reportDir: `${resultsFolder}/reports`,
55
+ reportFilename: '[name]-report.json'
56
+ },
62
57
  requestTimeout: 10000,
63
58
  retries: 0,
64
59
  screenshotsFolder: `${resultsFolder}/screenshots`,
60
+ setupNodeEvents: (on, config) => {
61
+ on('before:browser:launch', (browser, launchOptions) => {
62
+ if (browser.name === 'chrome' && browser.isHeadless) {
63
+ launchOptions.args = launchOptions.args.map((arg) => {
64
+ if (arg === '--headless') {
65
+ return '--headless=new';
66
+ }
67
+
68
+ return arg;
69
+ });
70
+ }
71
+
72
+ return launchOptions;
73
+ });
74
+ },
65
75
  video: true,
66
- videoCompression: 0,
67
76
  videosFolder: `${resultsFolder}/videos`
68
77
  });
69
78
  };
@@ -3,34 +3,132 @@
3
3
  /* eslint-disable @typescript-eslint/no-var-requires */
4
4
  /* eslint-disable no-param-reassign */
5
5
 
6
- export default (
7
- on: Cypress.PluginEvents,
8
- config: Cypress.PluginConfigOptions
9
- ): Cypress.PluginConfigOptions => {
10
- on('before:browser:launch', (browser, launchOptions) => {
11
- const width = 1920;
12
- const height = 1080;
13
-
14
- if (browser.name === 'chrome') {
15
- if (browser.isHeadless) {
16
- launchOptions.args.push('--headless=new');
17
- }
6
+ import Docker from 'dockerode';
7
+ import { addCucumberPreprocessorPlugin } from '@badeball/cypress-cucumber-preprocessor';
8
+ import webpackPreprocessor from '@cypress/webpack-preprocessor';
18
9
 
19
- // flags description : https://github.com/GoogleChrome/chrome-launcher/blob/main/docs/chrome-flags-for-tools.md
20
- launchOptions.args.push('--disable-gpu');
21
- launchOptions.args.push('--auto-open-devtools-for-tabs');
22
- launchOptions.args.push('--disable-extensions');
23
- launchOptions.args.push('--hide-scrollbars');
24
- launchOptions.args.push('--mute-audio');
10
+ const docker = new Docker();
11
+
12
+ const getWebpackOptions = (config): object => {
13
+ return {
14
+ module: {
15
+ rules: [
16
+ {
17
+ exclude: [/node_modules/],
18
+ test: /\.ts?$/,
19
+ use: [
20
+ {
21
+ loader: 'swc-loader'
22
+ }
23
+ ]
24
+ },
25
+ {
26
+ test: /\.feature$/,
27
+ use: [
28
+ {
29
+ loader: '@badeball/cypress-cucumber-preprocessor/webpack',
30
+ options: config
31
+ }
32
+ ]
33
+ }
34
+ ]
35
+ },
36
+ resolve: {
37
+ extensions: ['.ts', '.js']
38
+ }
39
+ };
40
+ };
41
+
42
+ export default async (on, config): Promise<void> => {
43
+ await addCucumberPreprocessorPlugin(on, config);
25
44
 
26
- launchOptions.args.push(`--window-size=${width},${height}`);
45
+ const webpackOptions = await getWebpackOptions(config);
46
+ const options = {
47
+ webpackOptions
48
+ };
27
49
 
28
- // force screen to be non-retina and just use our given resolution
29
- launchOptions.args.push('--force-device-scale-factor=1');
50
+ on('file:preprocessor', webpackPreprocessor(options));
51
+
52
+ on('before:browser:launch', (browser = {}, launchOptions) => {
53
+ if ((browser as { name }).name === 'chrome') {
54
+ launchOptions.args.push('--disable-gpu');
55
+ launchOptions.args = launchOptions.args.filter(
56
+ (element) => element !== '--disable-dev-shm-usage'
57
+ );
30
58
  }
31
59
 
32
60
  return launchOptions;
33
61
  });
34
62
 
63
+ interface PortBinding {
64
+ destination: number;
65
+ source: number;
66
+ }
67
+
68
+ interface StartContainerProps {
69
+ image: string;
70
+ name: string;
71
+ portBindings: Array<PortBinding>;
72
+ }
73
+
74
+ interface StopContainerProps {
75
+ name: string;
76
+ }
77
+
78
+ on('task', {
79
+ startContainer: async ({
80
+ image,
81
+ name,
82
+ portBindings = []
83
+ }: StartContainerProps) => {
84
+ const webContainers = await docker.listContainers({
85
+ all: true,
86
+ filters: { name: [name] }
87
+ });
88
+ if (webContainers.length) {
89
+ return webContainers[0];
90
+ }
91
+
92
+ const container = await docker.createContainer({
93
+ AttachStderr: true,
94
+ AttachStdin: false,
95
+ AttachStdout: true,
96
+ ExposedPorts: portBindings.reduce((accumulator, currentValue) => {
97
+ accumulator[`${currentValue.source}/tcp`] = {};
98
+
99
+ return accumulator;
100
+ }, {}),
101
+ HostConfig: {
102
+ PortBindings: portBindings.reduce((accumulator, currentValue) => {
103
+ accumulator[`${currentValue.source}/tcp`] = [
104
+ {
105
+ HostIP: '0.0.0.0',
106
+ HostPort: `${currentValue.destination}`
107
+ }
108
+ ];
109
+
110
+ return accumulator;
111
+ }, {})
112
+ },
113
+ Image: image,
114
+ OpenStdin: false,
115
+ StdinOnce: false,
116
+ Tty: true,
117
+ name
118
+ });
119
+
120
+ await container.start();
121
+
122
+ return container;
123
+ },
124
+ stopContainer: async ({ name }: StopContainerProps) => {
125
+ const container = await docker.getContainer(name);
126
+ await container.kill();
127
+ await container.remove();
128
+
129
+ return null;
130
+ }
131
+ });
132
+
35
133
  return config;
36
134
  };
@@ -21,15 +21,11 @@ module.exports = {
21
21
  }
22
22
  ],
23
23
  '@typescript-eslint/camelcase': 'off',
24
- '@typescript-eslint/consistent-type-definitions': ['off', 'interface'],
25
- '@typescript-eslint/explicit-function-return-type': [
24
+ '@typescript-eslint/consistent-type-definitions': [
26
25
  'error',
27
- {
28
- allowExpressions: true,
29
- allowHigherOrderFunctions: true,
30
- allowTypedFunctionExpressions: true
31
- }
26
+ 'interface'
32
27
  ],
28
+ '@typescript-eslint/explicit-function-return-type': ['error'],
33
29
  '@typescript-eslint/explicit-member-accessibility': [
34
30
  'error',
35
31
  {
@@ -80,21 +76,13 @@ module.exports = {
80
76
  }
81
77
  ],
82
78
  camelcase: 'off',
83
- 'import/no-cycle': 'off',
84
- 'import/no-named-as-default': 'warn',
85
79
  'no-shadow': 'off',
86
80
  'no-unused-expressions': 'off'
87
81
  },
88
82
  settings: {
89
- 'import/parsers': {
90
- '@typescript-eslint/parser': ['.ts', '.tsx']
91
- },
92
83
  'import/resolver': {
93
84
  alias: {
94
85
  extensions: ['.ts', '.tsx', '.js', '.jsx']
95
- },
96
- typescript: {
97
- alwaysTryTypes: true
98
86
  }
99
87
  }
100
88
  }
package/jest/index.js CHANGED
@@ -1,16 +1,12 @@
1
1
  module.exports = {
2
2
  moduleNameMapper: {
3
3
  '\\.(s?css|png|svg|jpg)$': 'identity-obj-proxy',
4
- '^.+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$':
5
- 'jest-transform-stub',
6
4
  '^react($|/.+)': '<rootDir>/node_modules/react$1'
7
5
  },
8
6
  setupFilesAfterEnv: ['@testing-library/jest-dom/extend-expect'],
9
7
  testEnvironment: 'jsdom',
10
- testPathIgnorePatterns: ['/node_modules/', '!*.cypress.spec.tsx'],
8
+ testPathIgnorePatterns: ['/node_modules/'],
11
9
  transform: {
12
- '.+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$':
13
- 'jest-transform-stub',
14
10
  '^.+\\.[jt]sx?$': [
15
11
  '@swc/jest',
16
12
  {
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": "23.10.46",
4
+ "version": "23.10.47",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/centreon/centreon-frontend.git"
@@ -16,12 +16,13 @@
16
16
  "url": "https://github.com/centreon/centreon-frontend/issues"
17
17
  },
18
18
  "devDependencies": {
19
- "@tsconfig/node16": "^16.1.1",
19
+ "@badeball/cypress-cucumber-preprocessor": "^14.0.0",
20
+ "@types/dockerode": "^3.3.16",
21
+ "dockerode": "^3.3.5",
20
22
  "eslint": "^8.17.0",
21
23
  "eslint-config-airbnb": "19.0.4",
22
24
  "eslint-config-prettier": "^8.5.0",
23
25
  "eslint-import-resolver-alias": "^1.1.2",
24
- "eslint-import-resolver-typescript": "^3.5.5",
25
26
  "eslint-plugin-babel": "^5.3.1",
26
27
  "eslint-plugin-hooks": "^0.4.3",
27
28
  "eslint-plugin-import": "^2.26.0",
@@ -42,18 +43,5 @@
42
43
  "tsconfig",
43
44
  "webpack",
44
45
  "cypress"
45
- ],
46
- "dependencies": {
47
- "@badeball/cypress-cucumber-preprocessor": "^18.0.6",
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.2",
52
- "@types/dockerode": "^3.3.19",
53
- "cypress-multi-reporters": "^1.6.3",
54
- "cypress-terminal-report": "^5.3.6",
55
- "dockerode": "^3.3.5",
56
- "esbuild": "^0.19.3",
57
- "mochawesome": "^7.1.3"
58
- }
46
+ ]
59
47
  }
@@ -1,14 +1,13 @@
1
1
  {
2
2
  "compilerOptions": {
3
- "downlevelIteration": true,
4
- "module": "ESNext",
5
3
  "moduleResolution": "node",
6
- "target": "es2018",
4
+ "downlevelIteration": true,
5
+ "module": "es6",
6
+ "target": "es6",
7
7
  "jsx": "react-jsx",
8
8
  "strict": true,
9
9
  "noImplicitAny": false,
10
10
  "skipLibCheck": true,
11
- "esModuleInterop": true,
12
- "resolveJsonModule": true
11
+ "esModuleInterop": true
13
12
  }
14
13
  }
@@ -3,22 +3,68 @@ const path = require('path');
3
3
  const { CleanWebpackPlugin } = require('clean-webpack-plugin');
4
4
  const { ModuleFederationPlugin } = require('webpack').container;
5
5
 
6
- const {
7
- getModuleConfiguration,
8
- optimization,
9
- output,
10
- cache
11
- } = require('./globalConfig');
6
+ const excludeNodeModulesExceptCentreonUi =
7
+ /node_modules(\\|\/)\.pnpm(\\|\/)(?!(@centreon))/;
12
8
 
13
9
  const getBaseConfiguration = ({
14
10
  moduleName,
15
11
  moduleFederationConfig,
16
12
  jscTransformConfiguration
17
13
  }) => ({
18
- cache,
19
- module: getModuleConfiguration(jscTransformConfiguration),
20
- optimization,
21
- output,
14
+ cache: false,
15
+ module: {
16
+ rules: [
17
+ {
18
+ parser: { system: false },
19
+ test: /\.[cm]?(j|t)sx?$/
20
+ },
21
+ {
22
+ exclude: excludeNodeModulesExceptCentreonUi,
23
+ test: /\.[jt]sx?$/,
24
+ use: {
25
+ loader: 'swc-loader',
26
+ options: {
27
+ jsc: {
28
+ parser: {
29
+ syntax: 'typescript',
30
+ tsx: true
31
+ },
32
+ transform: jscTransformConfiguration
33
+ }
34
+ }
35
+ }
36
+ },
37
+ {
38
+ test: /\.icon.svg$/,
39
+ use: ['@svgr/webpack']
40
+ },
41
+ {
42
+ exclude: excludeNodeModulesExceptCentreonUi,
43
+ test: /\.(bmp|png|jpg|jpeg|gif|svg)$/,
44
+ use: [
45
+ {
46
+ loader: 'url-loader',
47
+ options: {
48
+ limit: 10000,
49
+ name: '[name].[hash:8].[ext]'
50
+ }
51
+ }
52
+ ]
53
+ }
54
+ ]
55
+ },
56
+ optimization: {
57
+ splitChunks: {
58
+ chunks: 'all',
59
+ maxSize: 400 * 1024
60
+ }
61
+ },
62
+ output: {
63
+ chunkFilename: '[name].[chunkhash:8].chunk.js',
64
+ filename: '[name].[chunkhash:8].js',
65
+ libraryTarget: 'umd',
66
+ umdNamedDefine: true
67
+ },
22
68
  plugins: [
23
69
  new CleanWebpackPlugin(),
24
70
  moduleName &&
@@ -75,10 +121,7 @@ const getBaseConfiguration = ({
75
121
  ].filter(Boolean),
76
122
  resolve: {
77
123
  alias: {
78
- react: path.resolve('./node_modules/react'),
79
- '@centreon/ui/fonts': path.resolve(
80
- './node_modules/@centreon/ui/public/fonts'
81
- )
124
+ react: path.resolve('./node_modules/react')
82
125
  },
83
126
  extensions: ['.js', '.jsx', '.ts', '.tsx']
84
127
  }
@@ -1,75 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-namespace */
2
-
3
- const apiBase = '/centreon/api';
4
- const apiActionV1 = `${apiBase}/index.php`;
5
-
6
- const getStatusNumberFromString = (status: string): number => {
7
- const statuses = {
8
- critical: '2',
9
- down: '1',
10
- ok: '0',
11
- unknown: '3',
12
- unreachable: '2',
13
- up: '0',
14
- warning: '1'
15
- };
16
-
17
- if (status in statuses) {
18
- return statuses[status];
19
- }
20
-
21
- throw new Error(`Status ${status} does not exist`);
22
- };
23
-
24
- interface SubmitResult {
25
- host: string;
26
- output: string;
27
- perfdata?: string | null;
28
- service?: string | null;
29
- status: string;
30
- }
31
-
32
- Cypress.Commands.add(
33
- 'submitResults',
34
- (results: Array<SubmitResult>): Cypress.Chainable => {
35
- results.forEach(
36
- ({ host, output, perfdata = '', service = null, status }) => {
37
- const timestampNow = Math.floor(Date.now() / 1000) - 15;
38
- const updatetime = timestampNow.toString();
39
-
40
- const result = {
41
- host,
42
- output,
43
- perfdata,
44
- service,
45
- status: getStatusNumberFromString(status),
46
- updatetime
47
- };
48
-
49
- cy.request({
50
- body: {
51
- results: [result]
52
- },
53
- headers: {
54
- 'Content-Type': 'application/json',
55
- 'centreon-auth-token': window.localStorage.getItem('userTokenApiV1')
56
- },
57
- method: 'POST',
58
- url: `${apiActionV1}?action=submit&object=centreon_submit_results`
59
- });
60
- }
61
- );
62
-
63
- return cy.wrap(null);
64
- }
65
- );
66
-
67
- declare global {
68
- namespace Cypress {
69
- interface Chainable {
70
- submitResults: (props: Array<SubmitResult>) => Cypress.Chainable;
71
- }
72
- }
73
- }
74
-
75
- export {};
@@ -1,26 +0,0 @@
1
- import { NodeGlobalsPolyfillPlugin } from '@esbuild-plugins/node-globals-polyfill';
2
- import { NodeModulesPolyfillPlugin } from '@esbuild-plugins/node-modules-polyfill';
3
- import { addCucumberPreprocessorPlugin } from '@badeball/cypress-cucumber-preprocessor';
4
- import createBundler from '@bahmutov/cypress-esbuild-preprocessor';
5
- import createEsbuildPlugin from '@badeball/cypress-cucumber-preprocessor/esbuild';
6
-
7
- export default async (
8
- on: Cypress.PluginEvents,
9
- config: Cypress.PluginConfigOptions
10
- ): Promise<void> => {
11
- await addCucumberPreprocessorPlugin(on, config);
12
-
13
- on(
14
- 'file:preprocessor',
15
- createBundler({
16
- plugins: [
17
- createEsbuildPlugin(config),
18
- NodeModulesPolyfillPlugin(),
19
- NodeGlobalsPolyfillPlugin({
20
- buffer: true,
21
- process: true
22
- })
23
- ]
24
- })
25
- );
26
- };
@@ -1,13 +0,0 @@
1
- module.exports = {
2
- mochawesomeReporterOptions: {
3
- consoleReporter: 'none',
4
- html: false,
5
- json: true,
6
- overwrite: true,
7
- reportDir: 'results/reports',
8
- reportFilename: '[name]-report.json'
9
- },
10
- reporterEnabled: `mochawesome,${require.resolve(
11
- '@badeball/cypress-cucumber-preprocessor/pretty-reporter'
12
- )}`
13
- };
@@ -1,105 +0,0 @@
1
- import { execSync } from 'child_process';
2
- import { existsSync, mkdirSync } from 'fs';
3
-
4
- import Docker from 'dockerode';
5
-
6
- export default (on: Cypress.PluginEvents): void => {
7
- const docker = new Docker();
8
-
9
- interface PortBinding {
10
- destination: number;
11
- source: number;
12
- }
13
-
14
- interface StartContainerProps {
15
- image: string;
16
- name: string;
17
- portBindings: Array<PortBinding>;
18
- }
19
-
20
- interface StopContainerProps {
21
- name: string;
22
- }
23
-
24
- on('task', {
25
- createDirectory: async (directoryPath: string) => {
26
- if (!existsSync(directoryPath)) {
27
- mkdirSync(directoryPath, { recursive: true });
28
- }
29
-
30
- return null;
31
- },
32
- startContainer: async ({
33
- image,
34
- name,
35
- portBindings = []
36
- }: 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
- }
51
-
52
- const webContainers = await docker.listContainers({
53
- all: true,
54
- filters: { name: [name] }
55
- });
56
- if (webContainers.length) {
57
- return webContainers[0];
58
- }
59
-
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
- });
87
-
88
- await container.start();
89
-
90
- return container;
91
- },
92
- stopContainer: async ({ name }: StopContainerProps) => {
93
- const container = await docker.getContainer(name);
94
- await container.kill();
95
- await container.remove();
96
-
97
- return null;
98
- },
99
- waitOn: async (url: string) => {
100
- execSync(`npx wait-on ${url}`);
101
-
102
- return null;
103
- }
104
- });
105
- };
@@ -1,48 +0,0 @@
1
- module.exports = {
2
- extends: [ '../node/typescript.eslintrc.js'],
3
- overrides: [
4
- {
5
- files: ["*.spec.js", "*.test.ts", "*.tests.ts"],
6
- rules: {
7
- "import/first": 0,
8
- "import/order": 0,
9
- "@typescript-eslint/ban-ts-comment": 0,
10
- "@typescript-eslint/no-explicit-any": 0
11
- }
12
- }
13
- ],
14
- rules: {
15
- "import/extensions": ["off"],
16
- "no-console": "off",
17
- "no-underscore-dangle": "off",
18
- "class-methods-use-this": "off",
19
- "@typescript-eslint/naming-convention": [
20
- "error",
21
- {
22
- format: ["camelCase", "PascalCase", "UPPER_CASE"],
23
- selector: "variable"
24
- },
25
- {
26
- filter: {
27
- match: false,
28
- regex: "(__esModule|.+-.+)"
29
- },
30
- format: ["snake_case", "camelCase", "PascalCase", "UPPER_CASE"],
31
- selector: "property",
32
- leadingUnderscore: "allow"
33
- },
34
- {
35
- filter: {
36
- match: false,
37
- regex: "^_$"
38
- },
39
- format: ["snake_case", "camelCase", "PascalCase"],
40
- selector: "parameter"
41
- }
42
- ],
43
- "@typescript-eslint/require-array-sort-compare": "error"
44
- },
45
- parserOptions: {
46
- project: ["./tsconfig.json"]
47
- }
48
- }
@@ -1,49 +0,0 @@
1
- /*
2
- * Copyright 2023 Centreon Team
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
- // For a detailed explanation regarding each configuration property, visit:
18
- // https://jestjs.io/docs/en/configuration.html
19
- const path = require('path');
20
-
21
- const rootPath = path.join(__dirname);
22
-
23
-
24
-
25
- module.exports = {
26
- rootDir: rootPath,
27
- // Automatically clear mock calls and instances between every test
28
- clearMocks: true,
29
- // The directory where Jest should output its coverage files
30
- coverageDirectory: '<rootDir>/coverage',
31
- // An array of regexp pattern strings used to skip coverage collection
32
- coveragePathIgnorePatterns: ['\\\\node_modules\\\\', 'tests'],
33
-
34
- // An array of file extensions your modules use
35
- moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
36
-
37
- // Automatically reset mock state between every test
38
- // resetMocks: true,
39
-
40
- testMatch: ['**/*.(test|tests|spec|specs).+(ts|tsx|js)'],
41
-
42
- // This option allows the use of a custom results processor
43
- // testResultsProcessor: 'jest-sonar-reporter',
44
-
45
- // A map from regular expressions to paths to transformers
46
- transform: {
47
- '^.+\\.(ts|tsx)$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.json' }],
48
- },
49
- };
@@ -1,14 +0,0 @@
1
- {
2
- "extends": "@tsconfig/node16/tsconfig.json",
3
- "compilerOptions": {
4
- "sourceMap": true,
5
- "allowJs": true,
6
- "strictNullChecks": false,
7
- "declaration": false,
8
- "esModuleInterop": true,
9
- "types": [
10
- "@types/jest",
11
- "node"
12
- ]
13
- }
14
- }
@@ -1,71 +0,0 @@
1
- const excludeNodeModulesExceptCentreonUi =
2
- /node_modules(\\|\/)\.pnpm(\\|\/)(?!(@centreon))/;
3
-
4
- module.exports = {
5
- cache: false,
6
- excludeNodeModulesExceptCentreonUi,
7
- getModuleConfiguration: (jscTransformConfiguration) => ({
8
- rules: [
9
- {
10
- parser: { system: false },
11
- test: /\.[cm]?(j|t)sx?$/
12
- },
13
- {
14
- exclude: [excludeNodeModulesExceptCentreonUi],
15
- test: /\.[jt]sx?$/,
16
- use: {
17
- loader: 'swc-loader',
18
- options: {
19
- jsc: {
20
- parser: {
21
- syntax: 'typescript',
22
- tsx: true
23
- },
24
- transform: jscTransformConfiguration
25
- }
26
- }
27
- }
28
- },
29
- {
30
- test: /\.icon.svg$/,
31
- use: ['@svgr/webpack']
32
- },
33
- {
34
- exclude: excludeNodeModulesExceptCentreonUi,
35
- test: /\.(bmp|png|jpg|jpeg|gif|svg)$/,
36
- use: [
37
- {
38
- loader: 'url-loader',
39
- options: {
40
- limit: 10000,
41
- name: '[name].[hash:8].[ext]'
42
- }
43
- }
44
- ]
45
- },
46
- {
47
- generator: {
48
- filename: '[name][ext]'
49
- },
50
- test: /\.(woff|woff2|eot|ttf|otf)$/i,
51
- type: 'asset/resource'
52
- },
53
- {
54
- test: /\.css$/i,
55
- use: ['style-loader', 'css-loader']
56
- }
57
- ]
58
- }),
59
- optimization: {
60
- splitChunks: {
61
- chunks: 'all',
62
- maxSize: 400 * 1024
63
- }
64
- },
65
- output: {
66
- chunkFilename: '[name].[chunkhash:8].chunk.js',
67
- filename: '[name].[chunkhash:8].js',
68
- libraryTarget: 'umd',
69
- umdNamedDefine: true
70
- }
71
- };