From c4a7a9b03d332dfde9534d67183c326f6e39a630 Mon Sep 17 00:00:00 2001 From: Pierre Oucif Date: Fri, 20 Feb 2026 16:44:13 +0100 Subject: [PATCH 1/3] Return Ok(200) when empty body with 200 from browserRunner --- index.js | 28 ++++++++++++----- test/index.test.js | 78 +++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 94 insertions(+), 12 deletions(-) diff --git a/index.js b/index.js index 71833e1..a9aced1 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,11 @@ const core = require('@actions/core'); const { context, getOctokit } = require('@actions/github'); +async function parseJsonResponse(response) { + const text = await response.text(); + if (!text || text.trim() === '') return null; + return JSON.parse(text); +} async function createTestSummary(results, url) { const { runs, projectName, suiteName } = results; @@ -294,16 +299,19 @@ async function run() { throw new Error(`HTTP error! status: ${triggerResponse.status}`); } - const triggerData = await triggerResponse.json(); - const { executionId, url } = triggerData; + const triggerData = await parseJsonResponse(triggerResponse); + const executionId = triggerData?.executionId; + const url = triggerData?.url; - core.info(`Execution started with ID ${executionId}.`); - core.info(`execution-url: ${url}`); - core.setOutput('execution-id', executionId); - core.setOutput('execution-url', url); + if (executionId != null && url != null) { + core.info(`Execution started with ID ${executionId}.`); + core.info(`execution-url: ${url}`); + core.setOutput('execution-id', executionId); + core.setOutput('execution-url', url); + } // Decide whether to wait for results - if (waitForResults.toLowerCase() === 'yes' || waitForResults.toLowerCase() === 'true') { + if (executionId != null && url != null && (waitForResults.toLowerCase() === 'yes' || waitForResults.toLowerCase() === 'true')) { core.info(`Waiting for execution ${executionId} to finish...`); let status = 'running'; @@ -331,7 +339,11 @@ async function run() { throw new Error(`HTTP error! status: ${executionResponse.status}`); } - const report = await executionResponse.json(); + const report = await parseJsonResponse(executionResponse); + if (report == null) { + core.info('Execution status: no body, stopping wait.'); + break; + } status = report.status; core.info(`Execution status: ${status}`); diff --git a/test/index.test.js b/test/index.test.js index b7e21d8..4381139 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -109,20 +109,20 @@ describe('GitHub Action Tests', () => { describe('run', () => { beforeEach(() => { - // Mock fetch globally + // Mock fetch globally (parseJsonResponse uses text()) global.fetch = jest.fn().mockImplementation((url) => { if (url.includes('/trigger')) { return Promise.resolve({ ok: true, - json: () => Promise.resolve({ + text: () => Promise.resolve(JSON.stringify({ executionId: 'test-execution', url: 'http://test.com/execution' - }) + })) }); } return Promise.resolve({ ok: true, - json: () => Promise.resolve(mockResults) + text: () => Promise.resolve(JSON.stringify(mockResults)) }); }); }); @@ -203,6 +203,76 @@ describe('GitHub Action Tests', () => { expect(core.setFailed).toHaveBeenCalledWith(expect.stringContaining('API Error')); }); + it('should fail on HTTP error status from trigger', async () => { + core.getInput = jest.fn().mockImplementation((name) => { + if (name === 'suite-id') return 'test-suite-id'; + if (name === 'payload') return JSON.stringify({ stories: [{ id: 1, entryHref: 'http://example.com' }] }); + return null; + }); + global.fetch = jest.fn().mockResolvedValue({ + ok: false, + status: 500, + text: () => Promise.resolve('') + }); + + await run(); + + expect(core.setFailed).toHaveBeenCalledWith(expect.stringContaining('HTTP error! status: 500')); + }); + + it('should not fail when trigger response has empty body (fail only on HTTP status)', async () => { + core.getInput = jest.fn().mockImplementation((name) => { + if (name === 'suite-id') return 'test-suite-id'; + if (name === 'payload') return JSON.stringify({ stories: [{ id: 1, entryHref: 'http://example.com' }] }); + return null; + }); + global.fetch = jest.fn().mockImplementation((url) => { + if (url.includes('/trigger')) { + return Promise.resolve({ + ok: true, + text: () => Promise.resolve('') + }); + } + return Promise.resolve({ + ok: true, + text: () => Promise.resolve(JSON.stringify(mockResults)) + }); + }); + + await run(); + + expect(core.setFailed).not.toHaveBeenCalled(); + }); + + it('should not fail when execution status response has empty body, stop polling', async () => { + core.getInput = jest.fn().mockImplementation((name) => { + if (name === 'suite-id') return 'test-suite-id'; + if (name === 'payload') return JSON.stringify({ stories: [{ id: 1, entryHref: 'http://example.com' }] }); + return null; + }); + global.fetch = jest.fn().mockImplementation((url) => { + if (url.includes('/trigger')) { + return Promise.resolve({ + ok: true, + text: () => Promise.resolve(JSON.stringify({ + executionId: 'test-execution', + url: 'http://test.com/execution' + })) + }); + } + return Promise.resolve({ + ok: true, + text: () => Promise.resolve('') + }); + }); + + await run(); + + expect(core.setFailed).not.toHaveBeenCalled(); + const executionCalls = global.fetch.mock.calls.filter(call => call[0].includes('/execution/')); + expect(executionCalls.length).toBe(1); + }, 20000); + it('should handle invalid JSON payload', async () => { core.getInput = jest.fn().mockImplementation((name) => { if (name === 'payload') return 'invalid-json'; From 0061d6380ab951a81be692b451531f2c1ae96eeb Mon Sep 17 00:00:00 2001 From: Pierre Oucif Date: Fri, 20 Feb 2026 17:30:41 +0100 Subject: [PATCH 2/3] Remove not maintained stories --- .github/workflows/test-action.yaml | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/.github/workflows/test-action.yaml b/.github/workflows/test-action.yaml index bccf6f4..1763adf 100644 --- a/.github/workflows/test-action.yaml +++ b/.github/workflows/test-action.yaml @@ -51,34 +51,6 @@ jobs: wait-for-results: "yes" domain: "https://api-staging.heal.dev" comment-on-pr: "yes" - - name: Run Action - id: my-action_new - uses: ./ - with: - api-token: ${{ secrets.HEAL_API_TOKEN }} - suite: "wen/trigger" - test-config: | - { - "entrypoint": "https://www.wikipedia.org/", - "variables": { - "hello": "test level" - } - } - stories: | - [ - { - "slug": "new-test", - "test-config": { - "entrypoint": "https://www.ikea.com/fr/fr/", - "variables": { - "hello": "story level" - } - } - } - ] - wait-for-results: "yes" - domain: "https://api-staging.heal.dev" - - name: Run Action id: my-action_new_with_test_config uses: ./ From af7947b7f6d00bd95a6c10b855ad4c9cab945ed6 Mon Sep 17 00:00:00 2001 From: Pierre Oucif Date: Fri, 20 Feb 2026 17:50:03 +0100 Subject: [PATCH 3/3] Removing unstable test --- .github/workflows/test-action.yaml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/workflows/test-action.yaml b/.github/workflows/test-action.yaml index 1763adf..3ab320f 100644 --- a/.github/workflows/test-action.yaml +++ b/.github/workflows/test-action.yaml @@ -57,13 +57,6 @@ jobs: with: api-token: ${{ secrets.HEAL_API_TOKEN }} suite: "wen/trigger" - test-config: | - { - "entrypoint": "https://www.wikipedia.org/", - "variables": { - "hello": "you" - } - } stories: | [ {