# Instructions - Following Playwright test failed. - Explain why, be concise, respect Playwright best practices. - Provide a snippet of code with the fix, if possible. # Test info - Name: tssc/full_workflow.test.ts >> TSSC Complete Workflow >> Build Application Image >> should build application changes as new image through pipelines - Location: tests/tssc/full_workflow.test.ts:86:5 # Error details ``` Error: Pipeline not found or not yet running. Retrying... ``` # Test source ```ts 219 | */ 220 | export async function promoteWithoutPRAndGetPipeline( 221 | git: Git, 222 | ci: CI, 223 | cd: ArgoCD, 224 | environment: Environment, 225 | image: string 226 | ): Promise { 227 | logger.info(`Promoting application to ${environment} environment with direct commit...`); 228 | 229 | try { 230 | // Step 1: Check if target environment's application exists 231 | const application = await cd.getApplication(environment); 232 | expect(application).not.toBeNull(); 233 | logger.info(`Application exists in ${environment} environment`); 234 | 235 | // Step 2: Create a promotion commit to the gitops repository 236 | const commitSha = await git.createPromotionCommitOnGitOpsRepo(environment, image); 237 | logger.info(`Created commit with SHA: ${commitSha}`); 238 | 239 | //TODO: Remove this once we are using Jenkins Plugins in the future 240 | if (ci.getCIType() === CIType.JENKINS) { 241 | // Step 2: Trigger the Jenkins job because we are not using Jenkins Plugins 242 | await (ci as JenkinsCI).triggerPipeline(git.getGitOpsRepoName()); 243 | } 244 | 245 | // Create a pull request object for pipeline reference only 246 | // Note: This is not an actual PR, just a reference object with the commit SHA 247 | const commitRef = new PullRequest(0, commitSha, git.getGitOpsRepoName()); 248 | 249 | // Step 3: Wait for pipeline triggered by the promotion PR to complete 250 | const pipeline = await getPipelineAndWaitForCompletion( 251 | ci, 252 | commitRef, 253 | EventType.PUSH, 254 | `commit ${commitSha} on main branch in ${commitRef.repository}` 255 | ); 256 | 257 | // Step 4: Sync and wait for the application to be ready 258 | const syncResult = await runAndWaitforAppSync(cd, environment, commitSha); 259 | expect(syncResult).toBe(true); 260 | 261 | logger.info(`Application successfully promoted to ${environment}`); 262 | 263 | return pipeline; 264 | } catch (error) { 265 | logger.error( 266 | `Error directly promoting application to ${environment}: ${error}` 267 | ); 268 | throw error; 269 | } 270 | } 271 | 272 | export async function runAndWaitforAppSync( 273 | cd: ArgoCD, 274 | environment: Environment, 275 | commitSha: string 276 | ): Promise { 277 | try{ 278 | // Sync and wait for the application to be ready 279 | logger.info(`Syncing application in ${environment} environment`); 280 | await cd.syncApplication(environment); 281 | 282 | logger.info(`Waiting for application to sync in ${environment} environment...`); 283 | const syncResult = await cd.waitUntilApplicationIsSynced(environment, commitSha); 284 | if (!syncResult.synced) { 285 | throw new Error(`Failed to sync application. Status: ${syncResult.status}. Reason: ${syncResult.message}`); 286 | } 287 | logger.info(`Application successfully synced to ${environment}: ${syncResult.message}`); 288 | return syncResult.synced; 289 | } catch (error) { 290 | logger.error( 291 | `Error syncing and waiting for application to sync to ${environment} with commitSha ${commitSha}: ${error}` 292 | ); 293 | throw error; 294 | } 295 | } 296 | 297 | /** 298 | * Helper function to get pipeline and wait for completion with consistent error handling 299 | * @param ci CI provider instance 300 | * @param reference Pull request or commit reference 301 | * @param eventType Event type (PULL_REQUEST or PUSH) 302 | * @param operationDescription Description for logging 303 | * @returns Promise The completed pipeline 304 | */ 305 | export async function getPipelineAndWaitForCompletion( 306 | ci: CI, 307 | prReference: PullRequest, 308 | eventType: EventType, 309 | operationDescription: string 310 | ): Promise { 311 | const ciType = ci.getCIType(); 312 | 313 | try{ 314 | logger.info(`🔍 Getting ${ciType} pipeline for ${operationDescription}...`); 315 | const pipeline = await retry( 316 | async () => { 317 | const p = await ci.getPipeline(prReference, PipelineStatus.RUNNING, eventType); 318 | if (!p) { > 319 | throw new Error('Pipeline not found or not yet running. Retrying...'); | ^ Error: Pipeline not found or not yet running. Retrying... 320 | } 321 | return p; 322 | }, 323 | { 324 | retries: 10, 325 | minTimeout: 10000, 326 | maxTimeout: 50000, 327 | onRetry: (error: Error, attempt: number) => { 328 | logger.error(`Attempt ${attempt} failed: ${error}`); 329 | }, 330 | } 331 | ); 332 | 333 | if (!pipeline) { 334 | logger.error(`No ${ciType} pipeline was triggered by ${operationDescription}`); 335 | throw new Error('Expected a pipeline to be triggered but none was found'); 336 | } 337 | 338 | logger.info(`Pipeline ${pipeline.getDisplayName()} was triggered by ${operationDescription}`); 339 | 340 | const pipelineStatus = await ci.waitForPipelineToFinish(pipeline); 341 | logger.info(`${ciType} pipeline completed with status: ${pipelineStatus}`); 342 | 343 | await expectPipelineSuccess(pipeline, ci); 344 | logger.info(`${ciType} pipeline ${pipeline.getDisplayName()} was successful`); 345 | 346 | return pipeline; 347 | } catch (error) { 348 | logger.error( 349 | `Error waiting for pipeline: ${error}` 350 | ); 351 | throw error; 352 | } 353 | } 354 | 355 | export function getTestItemFromEnv(): TestItem { 356 | const raw = process.env.TESTITEM; 357 | if (!raw) { 358 | throw new Error('TESTITEM environment variable is not set'); 359 | } 360 | try { 361 | const obj = JSON.parse(raw); 362 | return new TestItem( 363 | obj.name, 364 | obj.template, 365 | obj.registryType, 366 | obj.gitType, 367 | obj.ciType, 368 | obj.tpa ?? '', 369 | obj.acs ?? '', 370 | obj.planName ?? 'legacy' 371 | ); 372 | } catch (e) { 373 | throw new Error(`Failed to parse TESTITEM: ${e}`); 374 | } 375 | } 376 | 377 | /** 378 | * Handles source repository code changes and manages pipeline execution based on CI provider type. 379 | * 380 | * This function implements different workflows depending on the CI system: 381 | * - For Jenkins: Creates a direct commit to the main branch and monitors the resulting pipeline 382 | * - For Tekton: Creates a pull request, waits for the PR pipeline to complete, merges the PR, 383 | * then waits for the push pipeline to complete 384 | * 385 | * @param git - The Git provider instance to interact with source repositories 386 | * @param ci - The CI provider instance to monitor pipeline execution 387 | * @returns Promise - Resolves when the entire workflow completes 388 | * @throws Error - If any step in the process fails 389 | */ 390 | export async function handleSourceRepoCodeChanges(git: Git, ci: CI): Promise { 391 | logger.info( 392 | 'Starting to make changes to source repo code and build application image through pipelines...' 393 | ); 394 | const ciType = ci.getCIType(); 395 | const gitType = git.getGitType(); 396 | 397 | if (ciType === CIType.GITHUB_ACTIONS || ciType === CIType.JENKINS || ciType === CIType.AZURE) { 398 | logger.info(`Using ${ciType} for ${gitType} repository`); 399 | // For GitHub Actions, we create a direct commit to the main branch 400 | return fastMovingToBuildApplicationImage(git, ci); 401 | } else { 402 | logger.info(`Creating a pull request on source repo on ${gitType} repository ...`); 403 | // For other CI types, create a PR which triggers a pipeline 404 | await buildApplicationImageWithPR(git, ci); 405 | } 406 | } 407 | 408 | /** 409 | * Expedites application image building by committing direct code changes and bypassing 410 | * the pull request workflow. 411 | * 412 | * This function: 413 | * 1. Makes source code changes directly to the main branch 414 | * 2. Triggers CI/CD pipeline to build the application image 415 | * 3. Promotes the built image to the specified environment 416 | * 4. Verifies successful deployment of the new image 417 | * 418 | * Use this method for testing scenarios where you need a quick image build without 419 | * the overhead of code reviews and pull request workflows. ```