From d5eabe8d35144e9cbfc743cc4aafc7a84627508c Mon Sep 17 00:00:00 2001 From: temp Date: Thu, 20 Feb 2025 21:56:36 +0000 Subject: [PATCH 1/5] no-op commit due to failed cherry-picking From 282b390427ba522efcff6dcd94d88b52d0699e4c Mon Sep 17 00:00:00 2001 From: Phil Renaud Date: Tue, 3 Dec 2024 09:56:06 -0500 Subject: [PATCH 2/5] [ui, ci] retain artifacts from test runs including test timing (#24555) * retain artifacts from test runs including test timing * Pinning commit hashes for action helpers * trigger for ui-test run * Trying to isolate down to a simple upload * Once more with mkdir * What if we just wrote our own test reporter tho * Let the partitioned runs handle placement * Filter out common token logs, add a summary at the end, and note failures in logtime * Custom reporter cannot also have an output file, he finds out two days late * Aggregate summary, duration, and removing failure case * Conditional test report generation * Timeouts are errors * Trying with un-partitioned input json file * Remove the commented-out lines for main-only runs * combine-ui-test-results as its own script --- .github/workflows/test-ui.yml | 40 +++++++- scripts/combine-ui-test-results.js | 51 +++++++++ ui/app/index.html | 1 + ui/test-reporter.js | 159 +++++++++++++++++++++++++++++ ui/testem.js | 23 +++++ 5 files changed, 269 insertions(+), 5 deletions(-) create mode 100644 scripts/combine-ui-test-results.js create mode 100644 ui/test-reporter.js diff --git a/.github/workflows/test-ui.yml b/.github/workflows/test-ui.yml index 14b1dd52900..26821b1ccf6 100644 --- a/.github/workflows/test-ui.yml +++ b/.github/workflows/test-ui.yml @@ -2,14 +2,14 @@ name: test-ui on: pull_request: paths: - - 'ui/**' + - "ui/**" push: branches: - main - release/** - test-ui paths: - - 'ui/**' + - "ui/**" jobs: pre-test: @@ -36,7 +36,6 @@ jobs: - pre-test runs-on: ${{ endsWith(github.repository, '-enterprise') && fromJSON('["self-hosted", "ondemand", "linux", "type=m7a.2xlarge;m6a.2xlarge"]') || 'ubuntu-latest' }} timeout-minutes: 30 - continue-on-error: true defaults: run: working-directory: ui @@ -44,6 +43,8 @@ jobs: matrix: partition: [1, 2, 3, 4] split: [4] + # Note: If we ever change the number of partitions, we'll need to update the + # finalize.combine step to match steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - uses: ./.github/actions/setup-js @@ -63,8 +64,19 @@ jobs: env: PERCY_TOKEN: ${{ env.PERCY_TOKEN || secrets.PERCY_TOKEN }} PERCY_PARALLEL_NONCE: ${{ needs.pre-test.outputs.nonce }} - run: yarn exam:parallel --split=${{ matrix.split }} --partition=${{ matrix.partition }} - + run: | + yarn exam:parallel --split=${{ matrix.split }} --partition=${{ matrix.partition }} --json-report=test-results/test-results.json + continue-on-error: true + - name: Express timeout failure + if: ${{ failure() }} + run: exit 1 + - name: Upload partition test results + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + with: + name: test-results-${{ matrix.partition }} + path: ui/test-results/test-results.json + retention-days: 90 finalize: needs: - pre-test @@ -88,6 +100,24 @@ jobs: jwtGithubAudience: ${{ vars.CI_VAULT_AUD }} secrets: |- kv/data/teams/nomad/ui PERCY_TOKEN ; + - name: Download all test results + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 + with: + pattern: test-results-* + path: test-results + + - name: Combine test results for comparison + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + run: node ../scripts/combine-ui-test-results.js + - name: Upload combined results for comparison + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + with: + name: test-results-${{ github.sha }} + path: ui/combined-test-results.json + retention-days: 90 + - name: finalize env: PERCY_TOKEN: ${{ env.PERCY_TOKEN || secrets.PERCY_TOKEN }} diff --git a/scripts/combine-ui-test-results.js b/scripts/combine-ui-test-results.js new file mode 100644 index 00000000000..8d78f423cce --- /dev/null +++ b/scripts/combine-ui-test-results.js @@ -0,0 +1,51 @@ +#!/usr/bin/env node +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +'use strict'; +const fs = require('fs'); + +const NUM_PARTITIONS = 4; + +function combineResults() { + const results = []; + let duration = 0; + let aggregateSummary = { total: 0, passed: 0, failed: 0 }; + + for (let i = 1; i <= NUM_PARTITIONS; i++) { + try { + const data = JSON.parse( + fs.readFileSync(`../test-results/test-results-${i}/test-results.json`).toString() + ); + results.push(...data.tests); + duration += data.duration; + aggregateSummary.total += data.summary.total; + aggregateSummary.passed += data.summary.passed; + aggregateSummary.failed += data.summary.failed; + } catch (err) { + console.error(`Error reading partition ${i}:`, err); + } + } + + const output = { + timestamp: new Date().toISOString(), + sha: process.env.GITHUB_SHA, + summary: { + total: aggregateSummary.total, + passed: aggregateSummary.passed, + failed: aggregateSummary.failed + }, + duration, + tests: results + }; + + fs.writeFileSync('../ui/combined-test-results.json', JSON.stringify(output, null, 2)); +} + +if (require.main === module) { + combineResults(); +} + +module.exports = combineResults; diff --git a/ui/app/index.html b/ui/app/index.html index 9d49fe8a579..df5eb739390 100644 --- a/ui/app/index.html +++ b/ui/app/index.html @@ -24,6 +24,7 @@ {{content-for "body"}} + {{content-for "body-footer"}} diff --git a/ui/test-reporter.js b/ui/test-reporter.js new file mode 100644 index 00000000000..ddad7268714 --- /dev/null +++ b/ui/test-reporter.js @@ -0,0 +1,159 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +/* eslint-env node */ +/* eslint-disable no-console */ + +const fs = require('fs'); +const path = require('path'); + +class JsonReporter { + constructor(out, socket, config) { + this.out = out || process.stdout; + this.results = []; + + // Get output file from Testem config, which is set by the --json-report=path argument + this.outputFile = config?.fileOptions?.custom_report_file; + this.generateReport = !!this.outputFile; + + if (this.generateReport) { + console.log( + `[Reporter] Initializing with output file: ${this.outputFile}` + ); + + try { + fs.mkdirSync(path.dirname(this.outputFile), { recursive: true }); + + // Initialize the results file + fs.writeFileSync( + this.outputFile, + JSON.stringify( + { + summary: { total: 0, passed: 0, failed: 0 }, + timestamp: new Date().toISOString(), + tests: [], + }, + null, + 2 + ) + ); + console.log('[Reporter] Initialized results file'); + } catch (err) { + console.error('[Reporter] Error initializing results file:', err); + } + } else { + console.log('[Reporter] No report file configured, skipping JSON output'); + } + + process.on('SIGINT', () => { + console.log('[Reporter] Received SIGINT, finishing up...'); + this.finish(); + process.exit(0); + }); + + this.testCounter = 0; + this.startTime = Date.now(); + } + + filterLogs(logs) { + return logs.filter((log) => { + // Filter out token-related logs + if ( + log.text && + (log.text.includes('Accessor:') || + log.text.includes('log in with a JWT') || + log.text === 'TOKENS:' || + log.text === '=====================================') + ) { + return false; + } + + // Keep non-warning logs that aren't token-related + return log.type !== 'warn'; + }); + } + + report(prefix, data) { + if (!data || !data.name) { + console.log(`[Reporter] Skipping invalid test result: ${data.name}`); + return; + } + + this.testCounter++; + console.log(`[Reporter] Test #${this.testCounter}: ${data.name}`); + + const partitionMatch = data.name.match(/^Exam Partition (\d+) - (.*)/); + + const result = { + name: partitionMatch ? partitionMatch[2] : data.name.trim(), + partition: partitionMatch ? parseInt(partitionMatch[1], 10) : null, + browser: prefix, + passed: !data.failed, + duration: data.runDuration, + error: data.failed ? data.error : null, + logs: this.filterLogs(data.logs || []), + }; + + if (result.passed) { + console.log('- [PASS]'); + } else { + console.log('- [FAIL]'); + console.log('- Error:', result.error); + console.log('- Logs:', result.logs); + } + + this.results.push(result); + } + + writeCurrentResults() { + console.log('[Reporter] Writing current results...'); + try { + const passed = this.results.filter((r) => r.passed).length; + const failed = this.results.filter((r) => !r.passed).length; + const total = this.results.length; + const duration = Date.now() - this.startTime; + + const output = { + summary: { total, passed, failed }, + timestamp: new Date().toISOString(), + duration, + tests: this.results, + }; + + if (this.generateReport) { + fs.writeFileSync(this.outputFile, JSON.stringify(output, null, 2)); + } + + // Print a summary + console.log('\n[Reporter] Test Summary:'); + console.log(`- Total: ${total}`); + console.log(`- Passed: ${passed}`); + console.log(`- Failed: ${failed}`); + console.log(`- Duration: ${duration}ms`); + if (failed > 0) { + console.log('\n[Reporter] Failed Tests:'); + this.results + .filter((r) => !r.passed) + .forEach((r) => { + console.log(`❌ ${r.name}`); + if (r.error) { + console.error(r.error); + } + }); + } + + console.log('[Reporter] Successfully wrote results'); + } catch (err) { + console.error('[Reporter] Error writing results:', err); + } + } + finish() { + console.log('[Reporter] Finishing up...'); + this.writeCurrentResults(); + console.log('[Reporter] Done.'); + } +} + +module.exports = JsonReporter; diff --git a/ui/testem.js b/ui/testem.js index 7d1869af9ef..c937a5760fe 100644 --- a/ui/testem.js +++ b/ui/testem.js @@ -3,7 +3,24 @@ * SPDX-License-Identifier: BUSL-1.1 */ +// @ts-check + 'use strict'; +const JsonReporter = require('./test-reporter'); + +/** + * Get the path for the test results file based on the command line arguments + * @returns {string} The path to the test results file + */ +const getReportPath = () => { + const jsonReportArg = process.argv.find((arg) => + arg.startsWith('--json-report=') + ); + if (jsonReportArg) { + return jsonReportArg.split('=')[1]; + } + return null; +}; const config = { test_page: 'tests/index.html?hidepassed', @@ -13,6 +30,12 @@ const config = { browser_start_timeout: 120, parallel: -1, framework: 'qunit', + reporter: JsonReporter, + custom_report_file: getReportPath(), + // NOTE: we output this property as custom_report_file instead of report_file. + // See https://github.com/testem/testem/issues/1073, report_file + custom reporter results in double output. + debug: true, + browser_args: { // New format in testem/master, but not in a release yet // Chrome: { From 2a3f3f055b4773d7b484d5c94d675683e8c2c52e Mon Sep 17 00:00:00 2001 From: Phil Renaud Date: Tue, 10 Dec 2024 11:25:53 -0600 Subject: [PATCH 3/5] Remove the Ember Test Audit workflow (#24637) From 7404d6f43e7aec908d4865da5d6d55f063c0b657 Mon Sep 17 00:00:00 2001 From: Phil Renaud Date: Thu, 20 Feb 2025 16:56:14 -0500 Subject: [PATCH 4/5] [ui, tests] Various acceptance test fixups (v main) (#25031) * Add factory hooks for jobs to have previously stable versions and stopped status * Since #24973 node-read isn't presupposed and so should regex match only on the common url parts * Job detail tests for title buttons are now bimodal and default to having previously-stable version in history * prettier plz * Breaking a thing on purpose to see if my other broken thing is broken * continue-on-error set to false to get things red when appropriate * OK what if continue-on-error=true but we do a separate failure reporting after the fact * fail-fast are you the magic incantation that I need? * Re-fix my test now that fast-fail is off * Fix to server-leader by adding a region first, and always()-append to uploading partition results * Express failure step lists failing tests so you don't have to click back into ember-exam step * temporary snapshot and logging for flakey test in service job detail * Bunch of region and tasklogs test fixups * using allocStatusDistribution to ensure service job always has a non-queued alloc --- .github/workflows/test-ui.yml | 22 ++++++++++++++----- ui/mirage/factories/job.js | 15 +++++++++++++ ui/tests/acceptance/job-detail-test.js | 18 ++++++++++++++- ui/tests/acceptance/regions-test.js | 20 ++++++++++++----- ui/tests/acceptance/server-detail-test.js | 1 + ui/tests/acceptance/task-logs-test.js | 5 +---- ui/tests/helpers/module-for-job.js | 6 ++++- .../components/job-page/periodic-test.js | 6 +++++ .../components/job-page/service-test.js | 17 +++++++++++--- .../integration/components/task-log-test.js | 18 ++++++++++++++- 10 files changed, 107 insertions(+), 21 deletions(-) diff --git a/.github/workflows/test-ui.yml b/.github/workflows/test-ui.yml index 26821b1ccf6..1be0872e9e5 100644 --- a/.github/workflows/test-ui.yml +++ b/.github/workflows/test-ui.yml @@ -40,6 +40,7 @@ jobs: run: working-directory: ui strategy: + fail-fast: false matrix: partition: [1, 2, 3, 4] split: [4] @@ -61,18 +62,29 @@ jobs: secrets: |- kv/data/teams/nomad/ui PERCY_TOKEN ; - name: ember exam + id: ember_exam env: PERCY_TOKEN: ${{ env.PERCY_TOKEN || secrets.PERCY_TOKEN }} PERCY_PARALLEL_NONCE: ${{ needs.pre-test.outputs.nonce }} run: | yarn exam:parallel --split=${{ matrix.split }} --partition=${{ matrix.partition }} --json-report=test-results/test-results.json continue-on-error: true - - name: Express timeout failure - if: ${{ failure() }} - run: exit 1 + - name: Express failure + if: steps.ember_exam.outcome == 'failure' + run: | + echo "Tests failed in ember-exam for partition ${{ matrix.partition }}" + echo "Failed tests:" + node -e " + const results = JSON.parse(require('fs').readFileSync('test-results/test-results.json')); + results.tests.filter(t => !t.passed).forEach(test => { + console.error('\n❌ ' + test.name); + if (test.error) console.error(test.error); + }); + " + exit 1 - name: Upload partition test results - if: github.event_name == 'push' && github.ref == 'refs/heads/main' - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + if: always() && github.event_name == 'push' && github.ref == 'refs/heads/main' + uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 with: name: test-results-${{ matrix.partition }} path: ui/test-results/test-results.json diff --git a/ui/mirage/factories/job.js b/ui/mirage/factories/job.js index ccaa605e4fa..f0cfe859a6c 100644 --- a/ui/mirage/factories/job.js +++ b/ui/mirage/factories/job.js @@ -175,6 +175,9 @@ export default Factory.extend({ // When true, the job will have no versions or deployments (and in turn no latest deployment) noDeployments: false, + // When true, the job will have a previous stable version. Useful for testing "start job" loop. + withPreviousStableVersion: false, + // When true, an evaluation with a high modify index and placement failures is created failedPlacements: false, @@ -317,8 +320,20 @@ export default Factory.extend({ version: index, noActiveDeployment: job.noActiveDeployment, activeDeployment: job.activeDeployment, + stable: true, }); }); + + if (job.withPreviousStableVersion) { + server.create('job-version', { + job, + namespace: job.namespace, + version: 1, + noActiveDeployment: job.noActiveDeployment, + activeDeployment: job.activeDeployment, + stable: true, + }); + } } if (job.activeDeployment) { diff --git a/ui/tests/acceptance/job-detail-test.js b/ui/tests/acceptance/job-detail-test.js index a525b8fc3a7..1896c743ca4 100644 --- a/ui/tests/acceptance/job-detail-test.js +++ b/ui/tests/acceptance/job-detail-test.js @@ -27,6 +27,7 @@ moduleForJob('Acceptance | job detail (batch)', 'allocations', () => allocStatusDistribution: { running: 1, }, + withPreviousStableVersion: true, }) ); @@ -39,6 +40,7 @@ moduleForJob('Acceptance | job detail (system)', 'allocations', () => allocStatusDistribution: { running: 1, }, + withPreviousStableVersion: true, }) ); @@ -52,6 +54,7 @@ moduleForJob('Acceptance | job detail (sysbatch)', 'allocations', () => running: 1, failed: 1, }, + withPreviousStableVersion: true, }) ); @@ -65,6 +68,7 @@ moduleForJobWithClientStatus( type: 'sysbatch', createAllocations: false, noActiveDeployment: true, + withPreviousStableVersion: true, }); } ); @@ -80,6 +84,7 @@ moduleForJobWithClientStatus( namespaceId: namespace.name, createAllocations: false, noActiveDeployment: true, + withPreviousStableVersion: true, }); } ); @@ -95,6 +100,7 @@ moduleForJobWithClientStatus( namespaceId: namespace.name, createAllocations: false, noActiveDeployment: true, + withPreviousStableVersion: true, }); } ); @@ -109,6 +115,7 @@ moduleForJob('Acceptance | job detail (sysbatch child)', 'allocations', () => { running: 1, }, noActiveDeployment: true, + withPreviousStableVersion: true, }); return server.db.jobs.where({ parentId: parent.id })[0]; }); @@ -212,6 +219,7 @@ moduleForJob( server.create('job', 'parameterized', { shallow: true, noActiveDeployment: true, + withPreviousStableVersion: true, }), { 'the default sort is submitTime descending': async (job, assert) => { @@ -292,7 +300,15 @@ moduleForJob( moduleForJob( 'Acceptance | job detail (service)', 'allocations', - () => server.create('job', { type: 'service', noActiveDeployment: true }), + () => + server.create('job', { + type: 'service', + noActiveDeployment: true, + withPreviousStableVersion: true, + allocStatusDistribution: { + running: 1, + }, + }), { 'the subnav links to deployment': async (job, assert) => { await JobDetail.tabFor('deployments').visit(); diff --git a/ui/tests/acceptance/regions-test.js b/ui/tests/acceptance/regions-test.js index a17529fd3b9..60131ca6990 100644 --- a/ui/tests/acceptance/regions-test.js +++ b/ui/tests/acceptance/regions-test.js @@ -5,7 +5,7 @@ /* eslint-disable qunit/require-expect */ /* eslint-disable qunit/no-conditional-assertions */ -import { currentURL } from '@ember/test-helpers'; +import { currentURL, settled } from '@ember/test-helpers'; import { module, test } from 'qunit'; import { setupApplicationTest } from 'ember-qunit'; import { selectChoose } from 'ember-power-select/test-support'; @@ -80,9 +80,11 @@ module('Acceptance | regions (only one)', function (hooks) { await JobsList.jobs.objectAt(0).clickRow(); await Layout.gutter.visitClients(); await Layout.gutter.visitServers(); - server.pretender.handledRequests.forEach((req) => { - assert.notOk(req.url.includes('region='), req.url); - }); + server.pretender.handledRequests + .filter((req) => !req.url.includes('/v1/status/leader')) + .forEach((req) => { + assert.notOk(req.url.includes('region='), req.url); + }); }); }); @@ -114,7 +116,10 @@ module('Acceptance | regions (many)', function (hooks) { }); test('when on the default region, pages do not include the region query param', async function (assert) { + let managementToken = server.create('token'); + window.localStorage.nomadTokenSecret = managementToken.secretId; await JobsList.visit(); + await settled(); assert.equal(currentURL(), '/jobs', 'No region query param'); assert.equal( @@ -143,11 +148,13 @@ module('Acceptance | regions (many)', function (hooks) { }); test('switching regions to the default region, unsets the region query param', async function (assert) { + let managementToken = server.create('token'); + window.localStorage.nomadTokenSecret = managementToken.secretId; const startingRegion = server.db.regions[1].id; const defaultRegion = server.db.regions[0].id; await JobsList.visit({ region: startingRegion }); - + await settled(); await selectChoose('[data-test-region-switcher-parent]', defaultRegion); assert.notOk( @@ -197,7 +204,8 @@ module('Acceptance | regions (many)', function (hooks) { const appRequests = server.pretender.handledRequests.filter( (req) => !req.responseURL.includes('/v1/regions') && - !req.responseURL.includes('/v1/operator/license') + !req.responseURL.includes('/v1/operator/license') && + !req.responseURL.includes('/v1/status/leader') ); assert.notOk( diff --git a/ui/tests/acceptance/server-detail-test.js b/ui/tests/acceptance/server-detail-test.js index d5420b1a4a2..7fe311fa02f 100644 --- a/ui/tests/acceptance/server-detail-test.js +++ b/ui/tests/acceptance/server-detail-test.js @@ -20,6 +20,7 @@ module('Acceptance | server detail', function (hooks) { hooks.beforeEach(async function () { server.createList('agent', 3); + server.create('region', { id: 'global' }); agent = server.db.agents[0]; await ServerDetail.visit({ name: agent.name }); }); diff --git a/ui/tests/acceptance/task-logs-test.js b/ui/tests/acceptance/task-logs-test.js index 83e0738d334..00b907e3394 100644 --- a/ui/tests/acceptance/task-logs-test.js +++ b/ui/tests/acceptance/task-logs-test.js @@ -62,10 +62,7 @@ module('Acceptance | task logs', function (hooks) { test('the stdout log immediately starts streaming', async function (assert) { await TaskLogs.visit({ id: allocation.id, name: task.name }); - const node = server.db.nodes.find(allocation.nodeId); - const logUrlRegex = new RegExp( - `${node.httpAddr}/v1/client/fs/logs/${allocation.id}` - ); + const logUrlRegex = new RegExp(`/v1/client/fs/logs/${allocation.id}`); assert.ok( server.pretender.handledRequests.filter((req) => logUrlRegex.test(req.url) diff --git a/ui/tests/helpers/module-for-job.js b/ui/tests/helpers/module-for-job.js index 5a73348a6e3..b02cf852368 100644 --- a/ui/tests/helpers/module-for-job.js +++ b/ui/tests/helpers/module-for-job.js @@ -92,7 +92,11 @@ export default function moduleForJob( test('the title buttons are dependent on job status', async function (assert) { if (job.status === 'dead') { - assert.ok(JobDetail.start.isPresent); + if (job.stopped) { + assert.ok(JobDetail.start.isPresent); + } else { + assert.ok(JobDetail.revert.isPresent); + } assert.ok(JobDetail.purge.isPresent); assert.notOk(JobDetail.stop.isPresent); assert.notOk(JobDetail.execButton.isPresent); diff --git a/ui/tests/integration/components/job-page/periodic-test.js b/ui/tests/integration/components/job-page/periodic-test.js index b6d665a25bf..f2e9b5cad57 100644 --- a/ui/tests/integration/components/job-page/periodic-test.js +++ b/ui/tests/integration/components/job-page/periodic-test.js @@ -202,6 +202,8 @@ module('Integration | Component | job-page/periodic', function (hooks) { childrenCount: 0, createAllocations: false, status: 'dead', + withPreviousStableVersion: true, + stopped: true, }); await this.store.findAll('job'); @@ -223,6 +225,8 @@ module('Integration | Component | job-page/periodic', function (hooks) { childrenCount: 0, createAllocations: false, status: 'dead', + withPreviousStableVersion: true, + stopped: true, }); await this.store.findAll('job'); @@ -243,6 +247,8 @@ module('Integration | Component | job-page/periodic', function (hooks) { childrenCount: 0, createAllocations: false, status: 'dead', + withPreviousStableVersion: true, + stopped: true, }); await this.store.findAll('job'); diff --git a/ui/tests/integration/components/job-page/service-test.js b/ui/tests/integration/components/job-page/service-test.js index 90156fcab81..994ec4bc615 100644 --- a/ui/tests/integration/components/job-page/service-test.js +++ b/ui/tests/integration/components/job-page/service-test.js @@ -112,7 +112,11 @@ module('Integration | Component | job-page/service', function (hooks) { test('Starting a job sends a post request for the job using the current definition', async function (assert) { assert.expect(1); - const mirageJob = makeMirageJob(this.server, { status: 'dead' }); + const mirageJob = makeMirageJob(this.server, { + status: 'dead', + withPreviousStableVersion: true, + stopped: true, + }); await this.store.findAll('job'); const job = this.store.peekAll('job').findBy('plainId', mirageJob.id); @@ -129,7 +133,11 @@ module('Integration | Component | job-page/service', function (hooks) { this.server.pretender.post('/v1/job/:id', () => [403, {}, '']); - const mirageJob = makeMirageJob(this.server, { status: 'dead' }); + const mirageJob = makeMirageJob(this.server, { + status: 'dead', + withPreviousStableVersion: true, + stopped: true, + }); await this.store.findAll('job'); const job = this.store.peekAll('job').findBy('plainId', mirageJob.id); @@ -144,7 +152,10 @@ module('Integration | Component | job-page/service', function (hooks) { test('Purging a job sends a purge request for the job', async function (assert) { assert.expect(1); - const mirageJob = makeMirageJob(this.server, { status: 'dead' }); + const mirageJob = makeMirageJob(this.server, { + status: 'dead', + withPreviousStableVersion: true, + }); await this.store.findAll('job'); const job = this.store.peekAll('job').findBy('plainId', mirageJob.id); diff --git a/ui/tests/integration/components/task-log-test.js b/ui/tests/integration/components/task-log-test.js index 54ed13a6c87..368014ac140 100644 --- a/ui/tests/integration/components/task-log-test.js +++ b/ui/tests/integration/components/task-log-test.js @@ -11,6 +11,7 @@ import hbs from 'htmlbars-inline-precompile'; import { componentA11yAudit } from 'nomad-ui/tests/helpers/a11y-audit'; import Pretender from 'pretender'; import { logEncode } from '../../../mirage/data/logs'; +import { startMirage } from 'nomad-ui/initializers/ember-cli-mirage'; const HOST = '1.1.1.1:1111'; const allowedConnectionTime = 100; @@ -36,7 +37,22 @@ let logMode = null; module('Integration | Component | task log', function (hooks) { setupRenderingTest(hooks); - hooks.beforeEach(function () { + hooks.beforeEach(async function () { + this.server = startMirage(); + const managementToken = this.server.create('token'); + window.localStorage.nomadTokenSecret = managementToken.secretId; + const tokenService = this.owner.lookup('service:token'); + const tokenPromise = tokenService.fetchSelfTokenAndPolicies.perform(); + const timeoutPromise = new Promise((_, reject) => { + setTimeout( + () => reject(new Error('Token fetch timed out after 3 seconds')), + 3000 + ); + }); + await Promise.race([tokenPromise, timeoutPromise]); + // ^--- TODO: noticed some flakiness in local testing; this is meant to suss it out in CI. + await settled(); + const handler = ({ queryParams }) => { let frames; let data; From 16c141641c4bbdd3fb75b64aee0f2fa01cd8607f Mon Sep 17 00:00:00 2001 From: Phil Renaud Date: Fri, 21 Feb 2025 00:30:02 -0500 Subject: [PATCH 5/5] Manually remove ember test audit (re-do #24637) --- .github/workflows/ember-test-audit.yml | 87 -------------------------- 1 file changed, 87 deletions(-) delete mode 100644 .github/workflows/ember-test-audit.yml diff --git a/.github/workflows/ember-test-audit.yml b/.github/workflows/ember-test-audit.yml deleted file mode 100644 index 6c1dce0a8c4..00000000000 --- a/.github/workflows/ember-test-audit.yml +++ /dev/null @@ -1,87 +0,0 @@ -name: Ember test audit comparison -on: - pull_request: - paths: - - '.github/workflows/ember*' - - 'ui/**' - -defaults: - run: - working-directory: ui - -# There’s currently no way to share steps between jobs so there’s a lot of duplication -# for running the audit for the base and PR. -jobs: - time-base: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - ref: ${{ github.event.pull_request.base.sha }} - - uses: nanasess/setup-chromedriver@42cc2998329f041de87dc3cfa33a930eacd57eaa # v2.2.2 - - name: Use Node.js - uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4 - with: - node-version: '18' - - run: yarn --frozen-lockfile - - run: mkdir -p /tmp/test-reports - - run: npx ember-test-audit 1 --json --output ../base-audit.json - - name: Upload result - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 - with: - name: base-audit - path: base-audit.json - time-pr: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: nanasess/setup-chromedriver@42cc2998329f041de87dc3cfa33a930eacd57eaa # v2.2.2 - - name: Use Node.js - uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4 - with: - node-version: '18' - - run: yarn --frozen-lockfile - - run: mkdir -p /tmp/test-reports - - run: npx ember-test-audit 1 --json --output ../pr-audit.json - - name: Upload result - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 - with: - name: pr-audit - path: pr-audit.json - compare: - needs: [time-base, time-pr] - runs-on: ubuntu-latest - steps: - - uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 - with: - name: base-audit - - uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 - with: - name: pr-audit - - uses: backspace/ember-test-audit-comparison-action@21e9492d0033bc7e84b6189ae94537a6ed045cfa # v2 - with: - base-report-path: base-audit.json - comparison-report-path: pr-audit.json - base-identifier: ${{ github.event.pull_request.base.ref }} - comparison-identifier: ${{ github.event.pull_request.head.sha }} - timing-output-path: audit-diff.md - flakiness-output-path: flakiness-report.md - - uses: marocchino/sticky-pull-request-comment@331f8f5b4215f0445d3c07b4967662a32a2d3e31 # v2.9.0 - with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - path: audit-diff.md - - name: Check for existence of flakiness report - id: check_file - uses: andstor/file-existence-action@076e0072799f4942c8bc574a82233e1e4d13e9d6 # v3.0.0 - with: - files: "flakiness-report.md" - - name: comment PR - if: steps.check_file.outputs.files_exists == 'true' - uses: mshick/add-pr-comment@b8f338c590a895d50bcbfa6c5859251edc8952fc # v2.8.2 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - message-path: flakiness-report.md -permissions: - contents: read - pull-requests: write