diff --git a/src/services/test-runner.js b/src/services/test-runner.js index 461c303..36384e4 100644 --- a/src/services/test-runner.js +++ b/src/services/test-runner.js @@ -24,6 +24,7 @@ import { runTests, } from '../test-runner/index.js'; import * as output from '../utils/output.js'; +import { writeSession as defaultWriteSession } from '../utils/session.js'; import { createBuildObject } from './build-manager.js'; export class TestRunner extends EventEmitter { @@ -46,6 +47,7 @@ export class TestRunner extends EventEmitter { getBuild, finalizeApiBuild, output, + writeSession: defaultWriteSession, createError: (message, code) => new VizzlyError(message, code), }; } @@ -94,7 +96,7 @@ export class TestRunner extends EventEmitter { } async createBuild(options, tdd) { - return createBuild({ + let buildId = await createBuild({ runOptions: options, tdd, config: this.config, @@ -105,6 +107,18 @@ export class TestRunner extends EventEmitter { output: this.deps.output, }, }); + + if (!tdd && buildId) { + let writeSession = this.deps.writeSession || defaultWriteSession; + writeSession({ + buildId, + branch: options?.branch, + commit: options?.commit, + parallelId: options?.parallelId, + }); + } + + return buildId; } async finalizeBuild(buildId, isTddMode, success, executionTime) { diff --git a/tests/commands/preview.test.js b/tests/commands/preview.test.js index 1442294..f745029 100644 --- a/tests/commands/preview.test.js +++ b/tests/commands/preview.test.js @@ -7,6 +7,8 @@ import { previewCommand, validatePreviewOptions, } from '../../src/commands/preview.js'; +import { TestRunner } from '../../src/services/test-runner.js'; +import { readSession, writeSession } from '../../src/utils/session.js'; /** * Create mock output object that tracks calls @@ -225,6 +227,89 @@ describe('previewCommand', () => { assert.strictEqual(capturedBuildId, 'session-build-123'); }); + it('uses build ID written by testRunner.createBuild', async () => { + let output = createMockOutput(); + let capturedBuildId = null; + let buildIdFromRunner = 'api-build-from-test-runner'; + let branch = 'feature/storybook-session'; + + let testRunner = new TestRunner( + { + apiKey: 'test-token', + apiUrl: 'https://api.test', + comparison: {}, + }, + {}, + { + deps: { + spawn: () => { + throw new Error('spawn should not be called in createBuild test'); + }, + createApiClient: () => ({}), + createApiBuild: async () => ({ id: buildIdFromRunner }), + getBuild: async () => ({ id: buildIdFromRunner }), + finalizeApiBuild: async () => {}, + output: { + debug: () => {}, + }, + writeSession: session => { + writeSession(session, { cwd: testDir, env: {} }); + }, + createError: (message, code) => { + let error = new Error(message); + error.code = code; + return error; + }, + }, + } + ); + + await testRunner.createBuild( + { + buildName: 'Storybook Build', + branch, + commit: 'abc123def456', + parallelId: 'parallel-227', + }, + false + ); + + await previewCommand( + distDir, + {}, + {}, + { + loadConfig: async () => ({ + apiKey: 'test-token', + apiUrl: 'https://api.test', + }), + readSession: options => + readSession({ + ...options, + cwd: testDir, + env: {}, + }), + detectBranch: async () => branch, + createApiClient: () => ({}), + getBuild: async () => ({ project: { isPublic: true } }), + uploadPreviewZip: async (_client, buildId) => { + capturedBuildId = buildId; + return { + previewUrl: 'https://preview.test', + uploaded: 3, + totalBytes: 1000, + newBytes: 800, + reusedBlobs: 0, + }; + }, + output, + exit: () => {}, + } + ); + + assert.strictEqual(capturedBuildId, buildIdFromRunner); + }); + it('uses explicit build ID from options over session', async () => { let output = createMockOutput(); let capturedBuildId = null; diff --git a/tests/services/test-runner.test.js b/tests/services/test-runner.test.js new file mode 100644 index 0000000..6d0bab7 --- /dev/null +++ b/tests/services/test-runner.test.js @@ -0,0 +1,88 @@ +import assert from 'node:assert'; +import { describe, it } from 'node:test'; +import { TestRunner } from '../../src/services/test-runner.js'; + +function createDeps(overrides = {}) { + return { + spawn: () => { + throw new Error('spawn should not be called in createBuild tests'); + }, + createApiClient: () => ({}), + createApiBuild: async () => ({ id: 'api-build-123' }), + getBuild: async () => ({ id: 'api-build-123' }), + finalizeApiBuild: async () => {}, + output: { + debug: () => {}, + }, + writeSession: () => {}, + createError: (message, code) => { + let error = new Error(message); + error.code = code; + return error; + }, + ...overrides, + }; +} + +describe('services/test-runner', () => { + describe('createBuild', () => { + it('writes session context for API builds', async () => { + let writtenSessions = []; + let deps = createDeps({ + writeSession: session => { + writtenSessions.push(session); + }, + }); + let testRunner = new TestRunner( + { + apiKey: 'test-key', + apiUrl: 'https://api.vizzly.dev', + comparison: {}, + }, + {}, + { deps } + ); + + let buildId = await testRunner.createBuild( + { + buildName: 'Storybook Build', + branch: 'feature/session', + commit: 'abc123def456', + parallelId: 'parallel-42', + }, + false + ); + + assert.strictEqual(buildId, 'api-build-123'); + assert.deepStrictEqual(writtenSessions, [ + { + buildId: 'api-build-123', + branch: 'feature/session', + commit: 'abc123def456', + parallelId: 'parallel-42', + }, + ]); + }); + + it('does not write session context for TDD builds', async () => { + let writeSessionCalled = false; + let deps = createDeps({ + writeSession: () => { + writeSessionCalled = true; + }, + }); + let testRunner = new TestRunner({}, {}, { deps }); + + let buildId = await testRunner.createBuild( + { + buildName: 'TDD Build', + branch: 'main', + }, + true + ); + + assert.ok(buildId.startsWith('build-')); + assert.strictEqual(writeSessionCalled, false); + }); + }); +});