> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vikingqa.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Run your tests from GitHub Actions

> Set up a workflow that starts your vikingQA suite on every push, waits for it, and turns the check red when a test fails.

Your workflow makes one call to vikingQA, waits for the suite to finish, and
fails when a test fails. GitHub shows that as a check, so you can require it
before a merge or put your deploy step behind it.

There is nothing to install in your repository. A token, a secret and the
workflow file below are the whole integration.

<Note>
  vikingQA runs the tests from the repository connected to your project, at the
  branch your environment names — not from a checkout in this workflow. So the
  job needs no `actions/checkout`, no Node.js and no Playwright. `curl` and `jq`
  are already on GitHub's `ubuntu-latest` runners.
</Note>

## Before you start

Two things your project needs, each set up once:

| What | Where to check |
| - | - |
| A connected repository, the one that holds your tests | **Project settings → Repository** |
| An environment, naming the branch those tests live on | **Project settings → Environments** |

You also need to be an organisation admin to create the token in step 1. A
token starts test runs with nobody watching, so creating one is an admin
action.

## Set it up

<Steps>
  <Step title="Create a project API token">
    Open **Project settings → API tokens** and create one. Name it after the
    pipeline that will use it, such as `github-actions`, so you know what to
    revoke later.

    The token is shown once, when you create it. Copy it straight into step 2 —
    you cannot read it again.

    A token belongs to one project and can do two things: start batches in that
    project, and read that project's batches.
  </Step>

  <Step title="Store it as a GitHub secret">
    In the repository that holds your workflow, go to **Settings → Secrets and
    variables → Actions → New repository secret**. Name it `VIKINGQA_TOKEN` and
    paste the token.

    Or from your terminal, with the [GitHub CLI](https://cli.github.com):

    ```bash theme={null}
    gh secret set VIKINGQA_TOKEN
    ```

    Never put the token in the workflow file itself. A workflow file is part of
    your repository, and anyone who can read the repository can read it.
  </Step>

  <Step title="Add the workflow">
    Create `.github/workflows/vikingqa.yml` with the file below, change
    `ENVIRONMENT` to the name of your environment, and push it.

    ```yaml .github/workflows/vikingqa.yml theme={null}
    name: vikingQA

    on:
      push:
        branches: [main]

    jobs:
      test:
        runs-on: ubuntu-latest
        steps:
          - name: Run the vikingQA suite and wait for the result
            id: vikingqa
            env:
              VIKINGQA_TOKEN: ${{ secrets.VIKINGQA_TOKEN }}
              API: https://app.vikingqa.io/api/v1
              ENVIRONMENT: staging
            run: |
              batch=$(curl -sS --fail-with-body -X POST "$API/batches" \
                -H "Authorization: Bearer $VIKINGQA_TOKEN" \
                -H "Content-Type: application/json" \
                -d "$(jq -n --arg environment "$ENVIRONMENT" --arg v "$GITHUB_SHA" \
                      '{environment: $environment, appVersion: $v}')")

              key=$(jq -r .key <<<"$batch")
              echo "Started $(jq -r .url <<<"$batch")"

              # Read the batch every 15 seconds, for up to an hour.
              for _ in $(seq 240); do
                batch=$(curl -sS --fail-with-body --retry 5 "$API/batches/$key" \
                  -H "Authorization: Bearer $VIKINGQA_TOKEN")
                [ "$(jq -r .finished <<<"$batch")" = "true" ] && break
                sleep 15
              done

              status=$(jq -r .status <<<"$batch")
              echo "status=$status" >> "$GITHUB_OUTPUT"
              echo "url=$(jq -r .url <<<"$batch")" >> "$GITHUB_OUTPUT"
              echo "vikingQA batch $key: $status"
              jq -r '.error.message // empty' <<<"$batch"
              [ "$status" = "PASSED" ]
    ```
  </Step>
</Steps>

That is it. The next push to `main` starts a batch, and the job's log holds a
link to it in vikingQA while it runs.

### What the file does

* **`appVersion` is `$GITHUB_SHA`.** vikingQA records it on the batch and shows
  it back to you, so you can tell which build a result is about. Any string
  your pipeline has works — a release tag, a build number.
* **`--fail-with-body` fails the step on any refusal** and prints why, so a
  wrong environment name is a red step with a readable message rather than a
  silent pass.
* **`--retry 5` rides out a brief network error while polling.** A blip does not
  fail your release.
* **The loop waits up to an hour.** Raise `240` for a longer suite: the job
  waits `15` seconds per turn, and GitHub's own ceiling is six hours per job.
* **The last line passes only on `PASSED`.** Anything else fails the step,
  including a suite still running when the hour is up. Decide for yourself
  whether `SKIPPED` should pass, and add it to that line if so.
* **The two `$GITHUB_OUTPUT` lines carry the result to any later step**, which
  is what the pull-request comment below reads.

## Gate a deploy on the result

Put your deploy in a second job that `needs` the test job. GitHub does not
start it unless the suite passed.

```yaml theme={null}
jobs:
  test:
    # …the job above, unchanged…

  deploy:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - run: ./deploy.sh
```

To block merges instead, make the check required: **repository Settings →
Branches → branch protection rule → Require status checks to pass**, and choose
`test`.

## Variations

Each of these is a change to the file above, not a new file.

### Run only your smoke tests on a pull request

Run a tagged subset on pull requests, where you want an answer in minutes. Two
changes to the file:

```yaml theme={null}
on:
  pull_request:
```

```bash theme={null}
-d "$(jq -n --arg environment "$ENVIRONMENT" --arg v "$GITHUB_SHA" \
      '{environment: $environment, appVersion: $v, tags: ["@smoke"]}')"
```

A test runs when it carries at least one of the tags you name. Write a tag as
it is in your code, `@smoke`, or without the `@` — both select the same tests.
Leave `tags` out to run everything.

If no test carries the tag, the batch **fails** rather than passing with
nothing tested.

To keep the whole suite on `main` as well, put this version in a second
workflow file rather than adding a trigger to the first: one file cannot send
`tags` for one event and not the other without a condition on every line that
uses it.

On a `pull_request` event `$GITHUB_SHA` is the merge commit GitHub builds, not
the head of your branch. Use `${{ github.event.pull_request.head.sha }}` if you
want the commit you pushed recorded as the application version.

### Run the whole suite every night

```yaml theme={null}
on:
  schedule:
    - cron: "0 2 * * *"
```

vikingQA can also run a schedule for you, with no workflow at all. Use this
form when you want the result in GitHub.

### Start it and move on

Drop the wait when your team reads the results in vikingQA rather than gating
on them. The whole step becomes:

```yaml theme={null}
- name: Start the vikingQA suite
  env:
    VIKINGQA_TOKEN: ${{ secrets.VIKINGQA_TOKEN }}
    ENVIRONMENT: staging
  run: |
    curl -sS --fail-with-body -X POST https://app.vikingqa.io/api/v1/batches \
      -H "Authorization: Bearer $VIKINGQA_TOKEN" \
      -H "Content-Type: application/json" \
      -d "$(jq -n --arg environment "$ENVIRONMENT" --arg v "$GITHUB_SHA" \
            '{environment: $environment, appVersion: $v}')" \
      | jq -r .url
```

The step still fails on a refusal, such as a wrong environment name. It does
not fail on a failing test, because it does not wait to find out.

### Comment the result on the pull request

Add this step after the one that waits. This is your own workflow commenting
with your own `GITHUB_TOKEN`; vikingQA does not write to your repository.

```yaml theme={null}
- name: Comment the vikingQA result
  if: always() && github.event_name == 'pull_request' && steps.vikingqa.outputs.status != ''
  uses: actions/github-script@v7
  env:
    STATUS: ${{ steps.vikingqa.outputs.status }}
    URL: ${{ steps.vikingqa.outputs.url }}
  with:
    github-token: ${{ secrets.GITHUB_TOKEN }}
    script: |
      const passed = process.env.STATUS === "PASSED";
      await github.rest.issues.createComment({
        owner: context.repo.owner,
        repo: context.repo.repo,
        issue_number: context.issue.number,
        body: `${passed ? "✅" : "❌"} vikingQA: **${process.env.STATUS}** — [open the batch](${process.env.URL})`,
      });
```

The job also needs permission to write to the pull request:

```yaml theme={null}
jobs:
  test:
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write
```

## When the step goes red

| What you see | What happened | What to do |
| - | - | - |
| `401` `unauthorized` | The token is missing, unknown or revoked. | Check the secret is named `VIKINGQA_TOKEN`. Create a new token if it was revoked. |
| `404` `environment_not_found` | The project has no environment with that name. | Compare `ENVIRONMENT` with **Project settings → Environments**. Case does not matter. |
| `409` `environment_not_runnable` | The project has no repository connected, or vikingQA has lost access to it. | Reconnect it in **Project settings → Repository**. |
| `400` `invalid_request` | A field is missing or misspelt, such as `app_version`. | The fields are `environment`, `appVersion`, `name` and `tags`. |
| The batch finishes `FAILED` | A test failed — or the branch holds no automated tests at all (`empty_suite`), or no test carries your tags (`empty_selection`). | Open the batch from the link in the log. |
| The batch finishes `ERRORED` | The batch could not run properly, for example vikingQA could not read the branch. | The log prints the reason; the batch's page says more. |
| The step fails with `status` `RUNNING` | The suite ran longer than the hour the loop waits. | Raise `240` in `seq 240`. |

The full list of statuses and error codes is in the
[batches API reference](/api-reference/batches).

## Other CI systems

The same two calls work anywhere there is a shell — AWS CodeBuild, GitLab CI,
Jenkins. Only the way you store the token and write the job changes. See the
[batches API reference](/api-reference/batches) for the endpoints on their own.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.