github.com/freiheit-com/kuberpult@v1.24.2-0.20240328135542-315d5630abe6/services/frontend-service/src/ui/components/ReleaseCard/ReleaseCard.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 { ReleaseCard, ReleaseCardProps } from './ReleaseCard'; 17 import { render } from '@testing-library/react'; 18 import { UpdateOverview, UpdateRolloutStatus } from '../../utils/store'; 19 import { MemoryRouter } from 'react-router-dom'; 20 import { 21 Environment, 22 EnvironmentGroup, 23 Priority, 24 Release, 25 RolloutStatus, 26 StreamStatusResponse, 27 UndeploySummary, 28 } from '../../../api/api'; 29 import { Spy } from 'spy4js'; 30 31 const mock_FormattedDate = Spy.mockModule('../FormattedDate/FormattedDate', 'FormattedDate'); 32 33 describe('Release Card', () => { 34 const getNode = (overrides: ReleaseCardProps) => ( 35 <MemoryRouter> 36 <ReleaseCard {...overrides} /> 37 </MemoryRouter> 38 ); 39 const getWrapper = (overrides: ReleaseCardProps) => render(getNode(overrides)); 40 41 type TestData = { 42 name: string; 43 props: { 44 app: string; 45 version: number; 46 }; 47 rels: Release[]; 48 environments: { [key: string]: Environment }; 49 }; 50 const data: TestData[] = [ 51 { 52 name: 'using a sample release - useRelease hook', 53 props: { app: 'test1', version: 2 }, 54 rels: [ 55 { 56 version: 2, 57 sourceMessage: 'test-rel', 58 undeployVersion: false, 59 sourceCommitId: 'commit123', 60 sourceAuthor: 'author', 61 prNumber: '666', 62 createdAt: new Date(2023, 6, 6), 63 displayVersion: '2', 64 }, 65 ], 66 environments: {}, 67 }, 68 { 69 name: 'using a full release - component test', 70 props: { app: 'test2', version: 2 }, 71 rels: [ 72 { 73 undeployVersion: false, 74 version: 2, 75 sourceMessage: 'test-rel', 76 sourceCommitId: '12s3', 77 sourceAuthor: 'test-author', 78 prNumber: '666', 79 createdAt: new Date(2002), 80 displayVersion: '2', 81 }, 82 ], 83 environments: {}, 84 }, 85 { 86 name: 'using a deployed release - useDeployedAt test', 87 props: { app: 'test2', version: 2 }, 88 rels: [ 89 { 90 version: 2, 91 sourceMessage: 'test-rel', 92 sourceCommitId: 'commit123', 93 sourceAuthor: 'test-author', 94 prNumber: '666', 95 undeployVersion: false, 96 createdAt: new Date(2023, 6, 6), 97 displayVersion: '2', 98 }, 99 ], 100 environments: { 101 foo: { 102 name: 'foo', 103 locks: {}, 104 distanceToUpstream: 0, 105 priority: 0, 106 applications: { 107 test2: { 108 version: 2, 109 queuedVersion: 0, 110 name: 'test2', 111 locks: {}, 112 undeployVersion: false, 113 }, 114 }, 115 }, 116 }, 117 }, 118 { 119 name: 'using an undeployed release - useDeployedAt test', 120 props: { app: 'test2', version: 2 }, 121 rels: [ 122 { 123 version: 2, 124 sourceMessage: 'test-rel', 125 sourceCommitId: 'commit123', 126 undeployVersion: false, 127 createdAt: new Date(2023, 6, 6), 128 sourceAuthor: 'test-author', 129 prNumber: '666', 130 displayVersion: '2', 131 }, 132 ], 133 environments: { 134 undeployed: { 135 name: 'undeployed', 136 locks: {}, 137 distanceToUpstream: 0, 138 priority: 0, 139 applications: { 140 test2: { 141 version: 3, 142 queuedVersion: 0, 143 name: 'test2', 144 locks: {}, 145 undeployVersion: false, 146 }, 147 }, 148 }, 149 }, 150 }, 151 { 152 name: 'using another environment - useDeployedAt test', 153 props: { app: 'test2', version: 2 }, 154 rels: [ 155 { 156 version: 2, 157 sourceMessage: 'test-rel', 158 sourceCommitId: 'commit123', 159 undeployVersion: false, 160 sourceAuthor: 'test-author', 161 prNumber: '666', 162 createdAt: new Date(2023, 6, 6), 163 displayVersion: '2', 164 }, 165 ], 166 environments: { 167 other: { 168 locks: {}, 169 distanceToUpstream: 0, 170 priority: 0, 171 name: 'other', 172 applications: { 173 test3: { 174 version: 3, 175 queuedVersion: 0, 176 name: 'test2', 177 locks: {}, 178 undeployVersion: false, 179 }, 180 }, 181 }, 182 }, 183 }, 184 ]; 185 186 describe.each(data)(`Renders a Release Card`, (testcase) => { 187 it(testcase.name, () => { 188 // given 189 mock_FormattedDate.FormattedDate.returns(<div>some formatted date</div>); 190 // when 191 UpdateOverview.set({ 192 applications: { 193 [testcase.props.app]: { 194 name: testcase.props.app, 195 releases: testcase.rels, 196 sourceRepoUrl: 'url', 197 undeploySummary: UndeploySummary.NORMAL, 198 team: 'no-team', 199 warnings: [], 200 }, 201 }, 202 environmentGroups: [], 203 }); 204 const { container } = getWrapper(testcase.props); 205 206 // then 207 if (testcase.rels[0].undeployVersion) { 208 expect(container.querySelector('.release__title')?.textContent).toContain('Undeploy Version'); 209 } else { 210 expect(container.querySelector('.release__title')?.textContent).toContain( 211 testcase.rels[0].sourceMessage 212 ); 213 } 214 215 if (testcase.rels[0].displayVersion) { 216 expect(container.querySelector('.release-version__display-version')?.textContent).toContain( 217 testcase.rels[0].displayVersion 218 ); 219 } else if (testcase.rels[0].sourceCommitId) { 220 expect(container.querySelector('.release-version__commit-id')?.textContent).toContain( 221 testcase.rels[0].sourceCommitId 222 ); 223 } 224 expect(container.querySelector('.env-group-chip-list-test')).not.toBeEmptyDOMElement(); 225 expect(container.querySelector('.release__status')).toBeNull(); 226 }); 227 }); 228 }); 229 230 describe('Release Card Rollout Status', () => { 231 const getNode = (overrides: ReleaseCardProps) => ( 232 <MemoryRouter> 233 <ReleaseCard {...overrides} /> 234 </MemoryRouter> 235 ); 236 const getWrapper = (overrides: ReleaseCardProps) => render(getNode(overrides)); 237 238 type TestData = { 239 name: string; 240 props: { 241 app: string; 242 version: number; 243 }; 244 rels: Release[]; 245 environmentGroups: EnvironmentGroup[]; 246 rolloutStatus: StreamStatusResponse[]; 247 expectedStatusIcon: RolloutStatus; 248 expectedRolloutDetails: { [name: string]: RolloutStatus }; 249 }; 250 const data: TestData[] = [ 251 { 252 name: 'shows success when it is deployed', 253 props: { app: 'test1', version: 2 }, 254 rels: [ 255 { 256 version: 2, 257 sourceMessage: 'test-rel', 258 undeployVersion: false, 259 sourceCommitId: 'commit123', 260 sourceAuthor: 'author', 261 prNumber: '666', 262 createdAt: new Date(2023, 6, 6), 263 displayVersion: '2', 264 }, 265 ], 266 environmentGroups: [ 267 { 268 environmentGroupName: 'dev', 269 environments: [ 270 { 271 name: 'development', 272 applications: { 273 test1: { 274 version: 2, 275 name: '', 276 locks: {}, 277 queuedVersion: 0, 278 undeployVersion: false, 279 }, 280 }, 281 locks: {}, 282 distanceToUpstream: 0, 283 priority: Priority.OTHER, 284 }, 285 { 286 name: 'development2', 287 applications: { 288 test1: { 289 version: 2, 290 name: '', 291 locks: {}, 292 queuedVersion: 0, 293 undeployVersion: false, 294 }, 295 }, 296 locks: {}, 297 distanceToUpstream: 0, 298 priority: Priority.OTHER, 299 }, 300 ], 301 priority: Priority.UNRECOGNIZED, 302 distanceToUpstream: 0, 303 }, 304 { 305 environmentGroupName: 'staging', 306 environments: [ 307 { 308 name: 'staging', 309 applications: { 310 test1: { 311 version: 2, 312 name: '', 313 locks: {}, 314 queuedVersion: 0, 315 undeployVersion: false, 316 }, 317 }, 318 locks: {}, 319 distanceToUpstream: 0, 320 priority: Priority.OTHER, 321 }, 322 ], 323 priority: Priority.UNRECOGNIZED, 324 distanceToUpstream: 0, 325 }, 326 ], 327 rolloutStatus: [ 328 { 329 environment: 'development', 330 application: 'test1', 331 version: 2, 332 rolloutStatus: RolloutStatus.ROLLOUT_STATUS_SUCCESFUL, 333 }, 334 { 335 environment: 'development2', 336 application: 'test1', 337 version: 2, 338 rolloutStatus: RolloutStatus.ROLLOUT_STATUS_SUCCESFUL, 339 }, 340 { 341 environment: 'staging', 342 application: 'test1', 343 version: 2, 344 rolloutStatus: RolloutStatus.ROLLOUT_STATUS_SUCCESFUL, 345 }, 346 ], 347 348 expectedStatusIcon: RolloutStatus.ROLLOUT_STATUS_SUCCESFUL, 349 expectedRolloutDetails: { 350 dev: RolloutStatus.ROLLOUT_STATUS_SUCCESFUL, 351 staging: RolloutStatus.ROLLOUT_STATUS_SUCCESFUL, 352 }, 353 }, 354 ]; 355 356 describe.each(data)(`Renders a Release Card`, (testcase) => { 357 it(testcase.name, () => { 358 // given 359 mock_FormattedDate.FormattedDate.returns(<div>some formatted date</div>); 360 // when 361 UpdateOverview.set({ 362 applications: { 363 [testcase.props.app]: { 364 name: testcase.props.app, 365 releases: testcase.rels, 366 sourceRepoUrl: 'url', 367 undeploySummary: UndeploySummary.NORMAL, 368 team: 'no-team', 369 warnings: [], 370 }, 371 }, 372 environmentGroups: testcase.environmentGroups, 373 }); 374 testcase.rolloutStatus.forEach(UpdateRolloutStatus); 375 const { container } = getWrapper(testcase.props); 376 // then 377 expect(container.querySelector('.release__status')).not.toBeNull(); 378 expect( 379 container.querySelector( 380 `.release__status .rollout__icon_${rolloutStatusName(testcase.expectedStatusIcon)}` 381 ) 382 ).not.toBeNull(); 383 for (const [envGroup, status] of Object.entries(testcase.expectedRolloutDetails)) { 384 const row = container.querySelector(`tr[key="${envGroup}"]`); 385 expect(row?.querySelector(`.rollout__description_${rolloutStatusName(status)}`)).not.toBeNull(); 386 } 387 }); 388 }); 389 }); 390 391 const rolloutStatusName = (status: RolloutStatus): string => { 392 switch (status) { 393 case RolloutStatus.ROLLOUT_STATUS_SUCCESFUL: 394 return 'successful'; 395 case RolloutStatus.ROLLOUT_STATUS_PROGRESSING: 396 return 'progressing'; 397 case RolloutStatus.ROLLOUT_STATUS_PENDING: 398 return 'pending'; 399 case RolloutStatus.ROLLOUT_STATUS_ERROR: 400 return 'error'; 401 case RolloutStatus.ROLLOUT_STATUS_UNHEALTHY: 402 return 'unhealthy'; 403 default: 404 return 'unknown'; 405 } 406 };