@centreon/js-config 24.4.6 → 24.4.8

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,6 +1,11 @@
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({ enableExtendedCollector: true });
4
9
 
5
10
  const apiLoginV2 = '/centreon/authentication/providers/configurations/local';
6
11
 
@@ -23,7 +28,7 @@ Cypress.Commands.add('getWebVersion', (): Cypress.Chainable => {
23
28
 
24
29
  Cypress.Commands.add('getIframeBody', (): Cypress.Chainable => {
25
30
  return cy
26
- .get('iframe#main-content')
31
+ .get('iframe#main-content', { timeout: 10000 })
27
32
  .its('0.contentDocument.body')
28
33
  .should('not.be.empty')
29
34
  .then(cy.wrap);
@@ -39,6 +44,13 @@ Cypress.Commands.add(
39
44
  }
40
45
  );
41
46
 
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
+
42
54
  interface NavigateToProps {
43
55
  page: string;
44
56
  rootItemNumber: number;
@@ -51,8 +63,13 @@ Cypress.Commands.add(
51
63
  if (subMenu) {
52
64
  cy.hoverRootMenuItem(rootItemNumber)
53
65
  .contains(subMenu)
54
- .trigger('mouseover', { force: true });
55
- cy.contains(page).click({ force: true });
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);
56
73
 
57
74
  return;
58
75
  }
@@ -85,29 +102,41 @@ Cypress.Commands.add(
85
102
 
86
103
  interface CopyFromContainerProps {
87
104
  destination: string;
105
+ name?: string;
88
106
  source: string;
89
107
  }
90
108
 
91
109
  Cypress.Commands.add(
92
110
  'copyFromContainer',
93
- ({ source, destination }: CopyFromContainerProps) => {
94
- return cy.exec(
95
- `docker cp ${Cypress.env('dockerName')}:${source} "${destination}"`
96
- );
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);
97
120
  }
98
121
  );
99
122
 
100
123
  interface CopyToContainerProps {
101
124
  destination: string;
125
+ name?: string;
102
126
  source: string;
103
127
  }
104
128
 
105
129
  Cypress.Commands.add(
106
130
  'copyToContainer',
107
- ({ source, destination }: CopyToContainerProps) => {
108
- return cy.exec(
109
- `docker cp ${source} ${Cypress.env('dockerName')}:${destination}`
110
- );
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);
111
140
  }
112
141
  );
113
142
 
@@ -118,7 +147,7 @@ interface LoginByTypeOfUserProps {
118
147
 
119
148
  Cypress.Commands.add(
120
149
  'loginByTypeOfUser',
121
- ({ jsonName, loginViaApi }): Cypress.Chainable => {
150
+ ({ jsonName = 'admin', loginViaApi = false }): Cypress.Chainable => {
122
151
  if (loginViaApi) {
123
152
  return cy
124
153
  .fixture(`users/${jsonName}.json`)
@@ -135,24 +164,25 @@ Cypress.Commands.add(
135
164
  .visit(`${Cypress.config().baseUrl}`)
136
165
  .wait('@getNavigationList');
137
166
  }
167
+
138
168
  cy.visit(`${Cypress.config().baseUrl}`)
139
169
  .fixture(`users/${jsonName}.json`)
140
170
  .then((credential) => {
141
- cy.getByLabel({ label: 'Alias', tag: 'input' }).type(credential.login);
171
+ cy.getByLabel({ label: 'Alias', tag: 'input' }).type(
172
+ `{selectAll}{backspace}${credential.login}`
173
+ );
142
174
  cy.getByLabel({ label: 'Password', tag: 'input' }).type(
143
- credential.password
175
+ `{selectAll}{backspace}${credential.password}`
144
176
  );
145
177
  })
146
178
  .getByLabel({ label: 'Connect', tag: 'button' })
147
179
  .click();
148
180
 
149
- return cy
150
- .get('.SnackbarContent-root > .MuiPaper-root')
151
- .then(($snackbar) => {
152
- if ($snackbar.text().includes('Login succeeded')) {
153
- cy.wait('@getNavigationList');
154
- }
155
- });
181
+ return cy.get('.MuiAlert-message').then(($snackbar) => {
182
+ if ($snackbar.text().includes('Login succeeded')) {
183
+ cy.wait('@getNavigationList');
184
+ }
185
+ });
156
186
  }
157
187
  );
158
188
 
@@ -210,29 +240,20 @@ interface StartContainerProps {
210
240
  Cypress.Commands.add(
211
241
  'startContainer',
212
242
  ({ name, image, portBindings }: StartContainerProps): Cypress.Chainable => {
213
- return cy
214
- .exec('docker image list --format "{{.Repository}}:{{.Tag}}"')
215
- .then(({ stdout }) => {
216
- if (
217
- stdout.match(
218
- new RegExp(
219
- `^${image.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')}`,
220
- 'm'
221
- )
222
- )
223
- ) {
224
- cy.log(`Local docker image found : ${image}`);
243
+ cy.log(`Starting container ${name} from image ${image}`);
225
244
 
226
- return cy.wrap(image);
227
- }
228
-
229
- cy.log(`Pulling remote docker image : ${image}`);
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
+ );
230
252
 
231
- return cy.exec(`docker pull ${image}`).then(() => cy.wrap(image));
232
- })
233
- .then((imageName) =>
234
- cy.task('startContainer', { image: imageName, name, portBindings })
235
- );
253
+ Cypress.Commands.add(
254
+ 'createDirectory',
255
+ (directoryPath: string): Cypress.Chainable => {
256
+ return cy.task('createDirectory', directoryPath);
236
257
  }
237
258
  );
238
259
 
@@ -247,7 +268,7 @@ Cypress.Commands.add(
247
268
  'startWebContainer',
248
269
  ({
249
270
  name = Cypress.env('dockerName'),
250
- os = 'alma9',
271
+ os = Cypress.env('WEB_IMAGE_OS'),
251
272
  useSlim = true,
252
273
  version = Cypress.env('WEB_IMAGE_VERSION')
253
274
  }: StartWebContainerProps = {}): Cypress.Chainable => {
@@ -262,12 +283,13 @@ Cypress.Commands.add(
262
283
  portBindings: [{ destination: 4000, source: 80 }]
263
284
  })
264
285
  .then(() => {
265
- const baseUrl = 'http://0.0.0.0:4000';
286
+ const baseUrl = 'http://127.0.0.1:4000';
266
287
 
267
288
  Cypress.config('baseUrl', baseUrl);
268
289
 
269
- return cy.exec(
270
- `npx wait-on ${baseUrl}/centreon/api/latest/platform/installation/status`
290
+ return cy.task(
291
+ 'waitOn',
292
+ `${baseUrl}/centreon/api/latest/platform/installation/status`
271
293
  );
272
294
  })
273
295
  .visit('/') // this is necessary to refresh browser cause baseUrl has changed (flash appears in video)
@@ -284,7 +306,7 @@ Cypress.Commands.add(
284
306
  ({
285
307
  name = Cypress.env('dockerName')
286
308
  }: StopWebContainerProps = {}): Cypress.Chainable => {
287
- const logDirectory = `cypress/results/logs/${Cypress.spec.name.replace(
309
+ const logDirectory = `results/logs/${Cypress.spec.name.replace(
288
310
  artifactIllegalCharactersMatcher,
289
311
  '_'
290
312
  )}/${Cypress.currentTest.title.replace(
@@ -294,25 +316,64 @@ Cypress.Commands.add(
294
316
 
295
317
  return cy
296
318
  .visitEmptyPage()
297
- .exec(`mkdir -p "${logDirectory}"`)
319
+ .createDirectory(logDirectory)
298
320
  .copyFromContainer({
299
321
  destination: `${logDirectory}/broker`,
322
+ name,
300
323
  source: '/var/log/centreon-broker'
301
324
  })
302
325
  .copyFromContainer({
303
326
  destination: `${logDirectory}/engine`,
327
+ name,
304
328
  source: '/var/log/centreon-engine'
305
329
  })
306
- .execInContainer({
307
- command: `bash -e <<EOF
308
- chmod 777 /var/log/centreon/centreon-web.log > /dev/null 2>&1 || :
309
- EOF`,
310
- name
311
- })
312
330
  .copyFromContainer({
313
331
  destination: `${logDirectory}/centreon`,
332
+ name,
314
333
  source: '/var/log/centreon'
315
334
  })
335
+ .copyFromContainer({
336
+ destination: `${logDirectory}/centreon-gorgone`,
337
+ name,
338
+ source: '/var/log/centreon-gorgone'
339
+ })
340
+ .then(() => {
341
+ if (Cypress.env('WEB_IMAGE_OS').includes('alma')) {
342
+ return cy.copyFromContainer({
343
+ destination: `${logDirectory}/php`,
344
+ name,
345
+ source: '/var/log/php-fpm'
346
+ });
347
+ }
348
+
349
+ return cy.copyFromContainer(
350
+ {
351
+ destination: `${logDirectory}/php8.1-fpm-centreon-error.log`,
352
+ name,
353
+ source: '/var/log/php8.1-fpm-centreon-error.log'
354
+ },
355
+ { failOnNonZeroExit: false }
356
+ );
357
+ })
358
+ .then(() => {
359
+ if (Cypress.env('WEB_IMAGE_OS').includes('alma')) {
360
+ return cy.copyFromContainer({
361
+ destination: `${logDirectory}/httpd`,
362
+ name,
363
+ source: '/var/log/httpd'
364
+ });
365
+ }
366
+
367
+ return cy.copyFromContainer(
368
+ {
369
+ destination: `${logDirectory}/apache2`,
370
+ name,
371
+ source: '/var/log/apache2'
372
+ },
373
+ { failOnNonZeroExit: false }
374
+ );
375
+ })
376
+ .exec(`chmod -R 755 "${logDirectory}"`)
316
377
  .stopContainer({ name });
317
378
  }
318
379
  );
@@ -324,9 +385,11 @@ interface StopContainerProps {
324
385
  Cypress.Commands.add(
325
386
  'stopContainer',
326
387
  ({ name }: StopContainerProps): Cypress.Chainable => {
388
+ cy.log(`Stopping container ${name}`);
389
+
327
390
  cy.exec(`docker logs ${name}`).then(({ stdout }) => {
328
391
  cy.writeFile(
329
- `cypress/results/logs/${Cypress.spec.name.replace(
392
+ `results/logs/${Cypress.spec.name.replace(
330
393
  artifactIllegalCharactersMatcher,
331
394
  '_'
332
395
  )}/${Cypress.currentTest.title.replace(
@@ -341,21 +404,192 @@ Cypress.Commands.add(
341
404
  }
342
405
  );
343
406
 
407
+ interface Dashboard {
408
+ description?: string;
409
+ name: string;
410
+ }
411
+
412
+ Cypress.Commands.add(
413
+ 'insertDashboardList',
414
+ (fixtureFile: string): Cypress.Chainable => {
415
+ return cy.fixture(fixtureFile).then((dashboardList) => {
416
+ cy.wrap(
417
+ Promise.all(
418
+ dashboardList.map((dashboardBody: Dashboard) =>
419
+ cy.insertDashboard({ ...dashboardBody })
420
+ )
421
+ )
422
+ );
423
+ });
424
+ }
425
+ );
426
+
427
+ Cypress.Commands.add(
428
+ 'insertDashboard',
429
+ (dashboardBody: Dashboard): Cypress.Chainable => {
430
+ return cy.request({
431
+ body: {
432
+ ...dashboardBody
433
+ },
434
+ method: 'POST',
435
+ url: '/centreon/api/latest/configuration/dashboards'
436
+ });
437
+ }
438
+ );
439
+
440
+ Cypress.Commands.add(
441
+ 'insertDashboardWithWidget',
442
+ (dashboardBody, patchBody) => {
443
+ cy.request({
444
+ body: {
445
+ ...dashboardBody
446
+ },
447
+ method: 'POST',
448
+ url: '/centreon/api/latest/configuration/dashboards'
449
+ }).then((response) => {
450
+ const dashboardId = response.body.id;
451
+ cy.waitUntil(
452
+ () => {
453
+ return cy
454
+ .request({
455
+ method: 'GET',
456
+ url: `/centreon/api/latest/configuration/dashboards/${dashboardId}`
457
+ })
458
+ .then((getResponse) => {
459
+ return getResponse.body && getResponse.body.id === dashboardId;
460
+ });
461
+ },
462
+ {
463
+ timeout: 10000
464
+ }
465
+ );
466
+ cy.request({
467
+ body: patchBody,
468
+ method: 'PATCH',
469
+ url: `/centreon/api/latest/configuration/dashboards/${dashboardId}`
470
+ });
471
+ });
472
+ }
473
+ );
474
+
475
+ interface ShareDashboardToUserProps {
476
+ dashboardName: string;
477
+ role: string;
478
+ userName: string;
479
+ }
480
+
481
+ interface ListingRequestResult {
482
+ body: {
483
+ result: Array<{
484
+ id: number;
485
+ }>;
486
+ };
487
+ }
488
+
489
+ interface PatchDashboardBody {
490
+ panels: Array<{
491
+ layout: {
492
+ height: number;
493
+ min_height: number;
494
+ min_width: number;
495
+ width: number;
496
+ x: number;
497
+ y: number;
498
+ };
499
+ name: string;
500
+ widget_settings: {
501
+ options: {
502
+ description: {
503
+ content: string;
504
+ enabled: boolean;
505
+ };
506
+ name: string;
507
+ };
508
+ };
509
+ widget_type: string;
510
+ }>;
511
+ }
512
+
513
+ Cypress.Commands.add(
514
+ 'shareDashboardToUser',
515
+ ({ dashboardName, userName, role }: ShareDashboardToUserProps): void => {
516
+ Promise.all([
517
+ cy.request({
518
+ method: 'GET',
519
+ url: `/centreon/api/latest/configuration/users?search={"name":"${userName}"}`
520
+ }),
521
+ cy.request({
522
+ method: 'GET',
523
+ url: `/centreon/api/latest/configuration/dashboards?search={"name":"${dashboardName}"}`
524
+ })
525
+ ]).then(
526
+ ([retrievedUser, retrievedDashboard]: [
527
+ ListingRequestResult,
528
+ ListingRequestResult
529
+ ]) => {
530
+ const userId = retrievedUser.body.result[0].id;
531
+ const dashboardId = retrievedDashboard.body.result[0].id;
532
+
533
+ cy.request({
534
+ body: {
535
+ id: userId,
536
+ role: `${role}`
537
+ },
538
+ method: 'POST',
539
+ url: `/centreon/api/latest/configuration/dashboards/${dashboardId}/access_rights/contacts`
540
+ });
541
+ }
542
+ );
543
+ }
544
+ );
545
+
546
+ Cypress.Commands.add('getTimeFromHeader', (): Cypress.Chainable => {
547
+ return cy
548
+ .get('header div[data-cy="clock"]', { timeout: 10000 })
549
+ .should('be.visible')
550
+ .then(($time) => {
551
+ const headerTime = $time.children()[1].textContent;
552
+ if (headerTime?.match(/\d+:\d+/)) {
553
+ cy.log(`header time is : ${headerTime}`);
554
+
555
+ return cy.wrap(headerTime);
556
+ }
557
+
558
+ throw new Error(`header time is not displayed`);
559
+ });
560
+ });
561
+
344
562
  declare global {
345
563
  namespace Cypress {
346
564
  interface Chainable {
347
- copyFromContainer: (props: CopyFromContainerProps) => Cypress.Chainable;
348
- copyToContainer: (props: CopyToContainerProps) => Cypress.Chainable;
565
+ clickSubRootMenuItem: (page: string) => Cypress.Chainable;
566
+ copyFromContainer: (
567
+ props: CopyFromContainerProps,
568
+ options?: Partial<Cypress.ExecOptions>
569
+ ) => Cypress.Chainable;
570
+ copyToContainer: (
571
+ props: CopyToContainerProps,
572
+ options?: Partial<Cypress.ExecOptions>
573
+ ) => Cypress.Chainable;
574
+ createDirectory: (directoryPath: string) => Cypress.Chainable;
349
575
  execInContainer: ({
350
576
  command,
351
577
  name
352
578
  }: ExecInContainerProps) => Cypress.Chainable;
353
579
  getIframeBody: () => Cypress.Chainable;
580
+ getTimeFromHeader: () => Cypress.Chainable;
354
581
  getWebVersion: () => Cypress.Chainable;
355
582
  hoverRootMenuItem: (rootItemNumber: number) => Cypress.Chainable;
583
+ insertDashboard: (dashboard: Dashboard) => Cypress.Chainable;
584
+ insertDashboardList: (fixtureFile: string) => Cypress.Chainable;
585
+ insertDashboardWithWidget: (
586
+ dashboard: Dashboard,
587
+ patch: PatchDashboardBody
588
+ ) => Cypress.Chainable;
589
+
356
590
  loginByTypeOfUser: ({
357
- jsonName = 'admin',
358
- loginViaApi = false
591
+ jsonName,
592
+ loginViaApi
359
593
  }: LoginByTypeOfUserProps) => Cypress.Chainable;
360
594
  moveSortableElement: (direction: string) => Cypress.Chainable;
361
595
  navigateTo: ({
@@ -363,6 +597,11 @@ declare global {
363
597
  rootItemNumber,
364
598
  subMenu
365
599
  }: NavigateToProps) => Cypress.Chainable;
600
+ shareDashboardToUser: ({
601
+ dashboardName,
602
+ userName,
603
+ role
604
+ }: ShareDashboardToUserProps) => Cypress.Chainable;
366
605
  startContainer: ({
367
606
  name,
368
607
  image
@@ -4,13 +4,18 @@
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
+ import { config as configDotenv } from 'dotenv';
7
9
 
8
- import setupNodeEvents from './plugins';
10
+ import esbuildPreprocessor from './esbuild-preprocessor';
11
+ import plugins from './plugins';
12
+ import tasks from './tasks';
9
13
 
10
14
  interface ConfigurationOptions {
11
15
  cypressFolder?: string;
12
16
  dockerName?: string;
13
17
  env?: Record<string, unknown>;
18
+ envFile?: string;
14
19
  isDevelopment?: boolean;
15
20
  specPattern: string;
16
21
  }
@@ -20,11 +25,14 @@ export default ({
20
25
  cypressFolder,
21
26
  isDevelopment,
22
27
  dockerName,
23
- env
28
+ env,
29
+ envFile
24
30
  }: ConfigurationOptions): Cypress.ConfigOptions => {
25
- const resultsFolder = `${cypressFolder || 'cypress'}/results${
26
- isDevelopment ? '/dev' : ''
27
- }`;
31
+ if (envFile) {
32
+ configDotenv({ path: envFile });
33
+ }
34
+
35
+ const resultsFolder = `${cypressFolder || '.'}/results`;
28
36
 
29
37
  const webImageVersion = execSync('git rev-parse --abbrev-ref HEAD')
30
38
  .toString('utf8')
@@ -35,44 +43,34 @@ export default ({
35
43
  defaultCommandTimeout: 6000,
36
44
  e2e: {
37
45
  excludeSpecPattern: ['*.js', '*.ts', '*.md'],
38
- setupNodeEvents,
39
- specPattern
46
+ fixturesFolder: 'fixtures',
47
+ reporter: require.resolve('cypress-multi-reporters'),
48
+ reporterOptions: {
49
+ configFile: `${__dirname}/reporter-config.js`
50
+ },
51
+ setupNodeEvents: async (on, config) => {
52
+ installLogsPrinter(on);
53
+ await esbuildPreprocessor(on, config);
54
+ tasks(on);
55
+
56
+ return plugins(on, config);
57
+ },
58
+ specPattern,
59
+ supportFile: 'support/e2e.{js,jsx,ts,tsx}'
40
60
  },
41
61
  env: {
42
62
  ...env,
43
- OPENID_IMAGE_VERSION: '23.04',
63
+ OPENID_IMAGE_VERSION: process.env.MAJOR || '24.04',
44
64
  WEB_IMAGE_OS: 'alma9',
45
65
  WEB_IMAGE_VERSION: webImageVersion,
46
66
  dockerName: dockerName || 'centreon-dev'
47
67
  },
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
- },
68
+ execTimeout: 60000,
57
69
  requestTimeout: 10000,
58
70
  retries: 0,
59
71
  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
- },
75
72
  video: true,
73
+ videoCompression: 0,
76
74
  videosFolder: `${resultsFolder}/videos`
77
75
  });
78
76
  };
@@ -0,0 +1,26 @@
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
+ };