github.com/freiheit-com/kuberpult@v1.24.2-0.20240328135542-315d5630abe6/services/frontend-service/src/ui/components/ReleaseDialog/ReleaseDialog.test.tsx (about)

     1  /*This file is part of kuberpult.
     2  
     3  Kuberpult is free software: you can redistribute it and/or modify
     4  it under the terms of the Expat(MIT) License as published by
     5  the Free Software Foundation.
     6  
     7  Kuberpult is distributed in the hope that it will be useful,
     8  but WITHOUT ANY WARRANTY; without even the implied warranty of
     9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    10  MIT License for more details.
    11  
    12  You should have received a copy of the MIT License
    13  along with kuberpult. If not, see <https://directory.fsf.org/wiki/License:Expat>.
    14  
    15  Copyright 2023 freiheit.com*/
    16  import { EnvironmentListItem, ReleaseDialog, ReleaseDialogProps } from './ReleaseDialog';
    17  import { fireEvent, render } from '@testing-library/react';
    18  import { UpdateAction, UpdateOverview, UpdateRolloutStatus, UpdateSidebar } from '../../utils/store';
    19  import { Environment, EnvironmentGroup, Priority, Release, RolloutStatus, UndeploySummary } from '../../../api/api';
    20  import { Spy } from 'spy4js';
    21  import { SideBar } from '../SideBar/SideBar';
    22  import { MemoryRouter } from 'react-router-dom';
    23  
    24  const mock_FormattedDate = Spy.mockModule('../FormattedDate/FormattedDate', 'FormattedDate');
    25  
    26  describe('Release Dialog', () => {
    27      const getNode = (overrides: ReleaseDialogProps) => (
    28          <MemoryRouter>
    29              <ReleaseDialog {...overrides} />
    30          </MemoryRouter>
    31      );
    32      const getWrapper = (overrides: ReleaseDialogProps) => render(getNode(overrides));
    33  
    34      interface dataT {
    35          name: string;
    36          props: ReleaseDialogProps;
    37          rels: Release[];
    38          envs: Environment[];
    39          envGroups: EnvironmentGroup[];
    40          expect_message: boolean;
    41          expect_queues: number;
    42          data_length: number;
    43          teamName: string;
    44          rolloutStatus?: {
    45              application: string;
    46              environment: string;
    47              rolloutStatus: RolloutStatus;
    48              rolloutStatusName: string;
    49          }[];
    50      }
    51      interface dataTLocks {
    52          name: string;
    53          props: ReleaseDialogProps;
    54          rels: Release[];
    55          envs: Environment[];
    56          envGroups: EnvironmentGroup[];
    57          expect_message: boolean;
    58          expect_queues: number;
    59          data_length: number;
    60          teamName: string;
    61      }
    62      const dataLocks: dataTLocks[] = [
    63          {
    64              name: 'without locks',
    65              props: {
    66                  app: 'test1',
    67                  version: 2,
    68              },
    69              rels: [
    70                  {
    71                      version: 2,
    72                      sourceMessage: 'test1',
    73                      sourceAuthor: 'test',
    74                      sourceCommitId: 'commit',
    75                      createdAt: new Date(2002),
    76                      undeployVersion: false,
    77                      prNumber: '#1337',
    78                      displayVersion: '2',
    79                  },
    80              ],
    81              envs: [
    82                  {
    83                      name: 'prod',
    84                      locks: {},
    85                      applications: {
    86                          test1: {
    87                              name: 'test1',
    88                              version: 2,
    89                              locks: {},
    90                              queuedVersion: 0,
    91                              undeployVersion: false,
    92                          },
    93                      },
    94                      distanceToUpstream: 0,
    95                      priority: Priority.UPSTREAM,
    96                  },
    97              ],
    98              envGroups: [
    99                  {
   100                      // this data should never appear (group with no envs with a well-defined priority), but we'll make it for the sake of the test.
   101                      distanceToUpstream: 0,
   102                      environmentGroupName: 'prod',
   103                      environments: [],
   104                      priority: Priority.UPSTREAM,
   105                  },
   106              ],
   107              expect_message: true,
   108              expect_queues: 0,
   109              data_length: 2,
   110              teamName: '',
   111          },
   112      ];
   113      const data: dataT[] = [
   114          {
   115              name: 'normal release',
   116              props: {
   117                  app: 'test1',
   118                  version: 2,
   119              },
   120              rels: [
   121                  {
   122                      version: 2,
   123                      sourceMessage: 'test1',
   124                      sourceAuthor: 'test',
   125                      sourceCommitId: 'commit',
   126                      createdAt: new Date(2002),
   127                      undeployVersion: false,
   128                      prNumber: '#1337',
   129                      displayVersion: '2',
   130                  },
   131              ],
   132              envs: [
   133                  {
   134                      name: 'prod',
   135                      locks: { envLock: { message: 'envLock', lockId: 'ui-envlock' } },
   136                      applications: {
   137                          test1: {
   138                              name: 'test1',
   139                              version: 2,
   140                              locks: { applock: { message: 'appLock', lockId: 'ui-applock' } },
   141                              queuedVersion: 0,
   142                              undeployVersion: false,
   143                          },
   144                      },
   145                      distanceToUpstream: 0,
   146                      priority: Priority.UPSTREAM,
   147                  },
   148              ],
   149              envGroups: [
   150                  {
   151                      // this data should never appear (group with no envs with a well-defined priority), but we'll make it for the sake of the test.
   152                      distanceToUpstream: 0,
   153                      environmentGroupName: 'prod',
   154                      environments: [],
   155                      priority: Priority.UPSTREAM,
   156                  },
   157              ],
   158              expect_message: true,
   159              expect_queues: 0,
   160              data_length: 2,
   161              teamName: '',
   162          },
   163          {
   164              name: 'normal release with deploymentMetadata set',
   165              props: {
   166                  app: 'test1',
   167                  version: 2,
   168              },
   169              rels: [
   170                  {
   171                      version: 2,
   172                      sourceMessage: 'test1',
   173                      sourceAuthor: 'test',
   174                      sourceCommitId: 'commit',
   175                      createdAt: new Date(2002),
   176                      undeployVersion: false,
   177                      prNumber: '#1337',
   178                      displayVersion: '2',
   179                  },
   180              ],
   181              envs: [
   182                  {
   183                      name: 'prod',
   184                      locks: { envLock: { message: 'envLock', lockId: 'ui-envlock' } },
   185                      applications: {
   186                          test1: {
   187                              name: 'test1',
   188                              version: 2,
   189                              locks: { applock: { message: 'appLock', lockId: 'ui-applock' } },
   190                              queuedVersion: 0,
   191                              undeployVersion: false,
   192                              deploymentMetaData: { deployAuthor: 'test', deployTime: '1688467491' },
   193                          },
   194                      },
   195                      distanceToUpstream: 0,
   196                      priority: Priority.UPSTREAM,
   197                  },
   198              ],
   199              envGroups: [
   200                  {
   201                      // this data should never appear (group with no envs with a well-defined priority), but we'll make it for the sake of the test.
   202                      distanceToUpstream: 0,
   203                      environmentGroupName: 'prod',
   204                      environments: [],
   205                      priority: Priority.UPSTREAM,
   206                  },
   207              ],
   208              expect_message: true,
   209              expect_queues: 0,
   210              data_length: 2,
   211              teamName: '',
   212          },
   213          {
   214              name: 'two envs release',
   215              props: {
   216                  app: 'test1',
   217                  version: 2,
   218              },
   219              envs: [
   220                  {
   221                      name: 'prod',
   222                      locks: { envLock: { message: 'envLock', lockId: 'ui-envlock' } },
   223                      applications: {
   224                          test1: {
   225                              name: 'test1',
   226                              version: 2,
   227                              locks: { applock: { message: 'appLock', lockId: 'ui-applock' } },
   228                              queuedVersion: 0,
   229                              undeployVersion: false,
   230                          },
   231                      },
   232                      distanceToUpstream: 0,
   233                      priority: Priority.UPSTREAM,
   234                  },
   235                  {
   236                      name: 'dev',
   237                      locks: { envLock: { message: 'envLock', lockId: 'ui-envlock' } },
   238                      applications: {
   239                          test1: {
   240                              name: 'test1',
   241                              version: 3,
   242                              locks: { applock: { message: 'appLock', lockId: 'ui-applock' } },
   243                              queuedVersion: 666,
   244                              undeployVersion: false,
   245                          },
   246                      },
   247                      distanceToUpstream: 0,
   248                      priority: Priority.UPSTREAM,
   249                  },
   250              ],
   251              envGroups: [
   252                  {
   253                      // this data should never appear (group with no envs with a well-defined priority), but we'll make it for the sake of the test.
   254                      distanceToUpstream: 0,
   255                      environmentGroupName: 'prod',
   256                      environments: [],
   257                      priority: Priority.UPSTREAM,
   258                  },
   259              ],
   260              rels: [
   261                  {
   262                      sourceCommitId: 'cafe',
   263                      sourceMessage: 'the other commit message 2',
   264                      version: 2,
   265                      createdAt: new Date(2002),
   266                      undeployVersion: false,
   267                      prNumber: 'PR123',
   268                      sourceAuthor: 'nobody',
   269                      displayVersion: '2',
   270                  },
   271                  {
   272                      sourceCommitId: 'cafe',
   273                      sourceMessage: 'the other commit message 3',
   274                      version: 3,
   275                      createdAt: new Date(2002),
   276                      undeployVersion: false,
   277                      prNumber: 'PR123',
   278                      sourceAuthor: 'nobody',
   279                      displayVersion: '3',
   280                  },
   281              ],
   282              rolloutStatus: [
   283                  {
   284                      application: 'test1',
   285                      environment: 'prod',
   286                      rolloutStatus: RolloutStatus.ROLLOUT_STATUS_PENDING,
   287                      rolloutStatusName: 'pending',
   288                  },
   289                  {
   290                      application: 'test1',
   291                      environment: 'dev',
   292                      rolloutStatus: RolloutStatus.ROLLOUT_STATUS_PROGRESSING,
   293                      rolloutStatusName: 'progressing',
   294                  },
   295              ],
   296              expect_message: true,
   297              expect_queues: 1,
   298              data_length: 5,
   299              teamName: 'test me team',
   300          },
   301          {
   302              name: 'undeploy version release',
   303              props: {
   304                  app: 'test1',
   305                  version: 4,
   306              },
   307              rels: [
   308                  {
   309                      version: 4,
   310                      sourceAuthor: 'test1',
   311                      sourceMessage: '',
   312                      sourceCommitId: '',
   313                      prNumber: '',
   314                      createdAt: new Date(2002),
   315                      undeployVersion: true,
   316                      displayVersion: '4',
   317                  },
   318              ],
   319              envs: [],
   320              envGroups: [],
   321              expect_message: false,
   322              expect_queues: 0,
   323              data_length: 0,
   324              teamName: '',
   325          },
   326      ];
   327  
   328      const setTheStore = (testcase: dataT) => {
   329          const asMap: { [key: string]: Environment } = {};
   330          testcase.envs.forEach((obj) => {
   331              asMap[obj.name] = obj;
   332          });
   333          UpdateOverview.set({
   334              applications: {
   335                  [testcase.props.app]: {
   336                      name: testcase.props.app,
   337                      releases: testcase.rels,
   338                      team: testcase.teamName,
   339                      sourceRepoUrl: 'url',
   340                      undeploySummary: UndeploySummary.NORMAL,
   341                      warnings: [],
   342                  },
   343              },
   344              environmentGroups: [
   345                  {
   346                      environmentGroupName: 'dev',
   347                      environments: testcase.envs,
   348                      distanceToUpstream: 2,
   349                      priority: Priority.UNRECOGNIZED,
   350                  },
   351              ],
   352          });
   353          const status = testcase.rolloutStatus;
   354          if (status !== undefined) {
   355              for (const app of status) {
   356                  UpdateRolloutStatus({
   357                      application: app.application,
   358                      environment: app.environment,
   359                      version: 1,
   360                      rolloutStatus: app.rolloutStatus,
   361                  });
   362              }
   363          }
   364      };
   365  
   366      describe.each(data)(`Renders a Release Dialog`, (testcase) => {
   367          it(testcase.name, () => {
   368              // when
   369              setTheStore(testcase);
   370              getWrapper(testcase.props);
   371              if (testcase.expect_message) {
   372                  expect(document.querySelector('.release-dialog-message')?.textContent).toContain(
   373                      testcase.rels[0].sourceMessage
   374                  );
   375              } else {
   376                  expect(document.querySelector('.release-dialog-message') === undefined);
   377              }
   378              expect(document.querySelectorAll('.env-card-data')).toHaveLength(testcase.data_length);
   379              expect(document.querySelectorAll('.env-card-data-queue')).toHaveLength(testcase.expect_queues);
   380          });
   381      });
   382  
   383      describe.each(data)(`Renders the environment cards`, (testcase) => {
   384          it(testcase.name, () => {
   385              // when
   386              setTheStore(testcase);
   387              getWrapper(testcase.props);
   388              expect(document.querySelector('.release-env-list')?.children).toHaveLength(testcase.envs.length);
   389          });
   390      });
   391  
   392      describe.each(data)(`Renders the environment locks`, (testcase) => {
   393          it(testcase.name, () => {
   394              // given
   395              mock_FormattedDate.FormattedDate.returns(<div>some formatted date</div>);
   396              // when
   397              setTheStore(testcase);
   398              getWrapper(testcase.props);
   399              expect(document.body).toMatchSnapshot();
   400              expect(document.querySelectorAll('.release-env-group-list')).toHaveLength(1);
   401  
   402              testcase.envs.forEach((env) => {
   403                  expect(document.querySelector('.env-locks')?.children).toHaveLength(Object.values(env.locks).length);
   404              });
   405          });
   406      });
   407  
   408      describe.each(data)(`Renders the queuedVersion`, (testcase) => {
   409          it(testcase.name, () => {
   410              // when
   411              setTheStore(testcase);
   412              getWrapper(testcase.props);
   413              expect(document.querySelectorAll('.env-card-data-queue')).toHaveLength(testcase.expect_queues);
   414          });
   415      });
   416  
   417      describe.each(data)(`Renders the rollout status`, (testcase) => {
   418          const status = testcase.rolloutStatus;
   419          if (status === undefined) {
   420              return;
   421          }
   422          it(testcase.name, () => {
   423              const statusCount: { [status: string]: number } = {};
   424              for (const app of status) {
   425                  statusCount[app.rolloutStatusName] = (statusCount[app.rolloutStatusName] ?? 0) + 1;
   426              }
   427              // when
   428              setTheStore(testcase);
   429              getWrapper(testcase.props);
   430              for (const [descr, count] of Object.entries(statusCount)) {
   431                  expect(document.querySelectorAll('.rollout__description_' + descr)).toHaveLength(count);
   432              }
   433          });
   434      });
   435  
   436      const querySelectorSafe = (selectors: string): Element => {
   437          const result = document.querySelector(selectors);
   438          if (!result) {
   439              throw new Error('did not find in selector in document ' + selectors);
   440          }
   441          return result;
   442      };
   443  
   444      describe(`Test automatic cart opening`, () => {
   445          it('Test using direct call to open function', () => {
   446              UpdateSidebar.set({ shown: false });
   447              UpdateSidebar.set({ shown: true });
   448              expect(UpdateSidebar.get().shown).toBeTruthy();
   449          });
   450  
   451          describe.each(dataLocks)('click handling', (testcase) => {
   452              it('Test using deploy button click simulation ' + testcase.name, () => {
   453                  UpdateSidebar.set({ shown: false });
   454                  UpdateAction.set({ actions: [] });
   455                  setTheStore(testcase);
   456  
   457                  render(
   458                      <EnvironmentListItem
   459                          env={testcase.envs[0]}
   460                          envGroup={testcase.envGroups[0]}
   461                          app={testcase.props.app}
   462                          queuedVersion={0}
   463                          release={{ ...testcase.rels[0], version: 3 }}
   464                      />
   465                  );
   466                  const result = querySelectorSafe('.env-card-deploy-btn');
   467                  fireEvent.click(result);
   468                  expect(UpdateSidebar.get().shown).toBeTruthy();
   469                  expect(UpdateAction.get().actions).toEqual([
   470                      {
   471                          action: {
   472                              $case: 'deploy',
   473                              deploy: {
   474                                  application: 'test1',
   475                                  environment: 'prod',
   476                                  ignoreAllLocks: false,
   477                                  lockBehavior: 2,
   478                                  version: 3,
   479                              },
   480                          },
   481                      },
   482                      {
   483                          action: {
   484                              $case: 'createEnvironmentApplicationLock',
   485                              createEnvironmentApplicationLock: {
   486                                  application: 'test1',
   487                                  environment: 'prod',
   488                                  lockId: '',
   489                                  message: '',
   490                              },
   491                          },
   492                      },
   493                  ]);
   494              });
   495          });
   496          it('Test using add lock button click simulation', () => {
   497              const testcase = data[0];
   498              UpdateSidebar.set({ shown: false });
   499              UpdateAction.set({ actions: [] });
   500              setTheStore(testcase);
   501  
   502              getWrapper(testcase.props);
   503              render(
   504                  <EnvironmentListItem
   505                      env={testcase.envs[0]}
   506                      envGroup={testcase.envGroups[0]}
   507                      app={testcase.props.app}
   508                      queuedVersion={0}
   509                      release={testcase.rels[0]}
   510                  />
   511              );
   512              render(<SideBar toggleSidebar={Spy()} />);
   513              const result = querySelectorSafe('.env-card-add-lock-btn');
   514              fireEvent.click(result);
   515              expect(UpdateSidebar.get().shown).toBeTruthy();
   516              expect(UpdateAction.get().actions).toEqual([
   517                  {
   518                      action: {
   519                          $case: 'createEnvironmentApplicationLock',
   520                          createEnvironmentApplicationLock: {
   521                              application: 'test1',
   522                              environment: 'prod',
   523                              lockId: '',
   524                              message: '',
   525                          },
   526                      },
   527                  },
   528              ]);
   529          });
   530      });
   531  });