import { readFileSync } from "node:fs"; import path from "node:path"; import { describe, expect, it } from "vitest"; import { parse } from "yaml"; const workflowText = readFileSync( path.resolve(import.meta.dirname, "../../../.github/workflows/ci.yml"), "utf8", ); interface WorkflowJob { if?: string; needs?: string[]; "continue-on-error"?: boolean; } const workflow = parse(workflowText) as { jobs: Record; }; describe("ci workflow merge gate", () => { it("aggregates only product jobs in Product CI", () => { const productGate = workflow.jobs["product-gate"]; expect(productGate?.needs).toEqual([ "lint", "check", "build", "test", "test-integration", "test-e2e", ]); expect(productGate?.if).toBe("always()"); }); it("soft-fails mobile OTA so missing Expo credentials do not fail the workflow", () => { const mobile = workflow.jobs["publish-mobile-update"]; expect(mobile?.["continue-on-error"]).toBe(true); expect(workflowText).toContain('echo "ready=false" >> "$GITHUB_OUTPUT"'); expect(workflowText).not.toMatch( /Check Expo automation token[\s\S]*?exit 1[\s\S]*?Require Expo/, ); expect(workflowText).not.toContain( "Add an EXPO_TOKEN repository secret with access to the inbox-zero/rakazo Expo project.", ); expect(workflowText).toContain("steps.expo_token.outputs.ready == 'true'"); }); });