← All posts
Guide

Keep your tests in your repo: two-way Git sync with TestVibe

Your application code lives in Git. Your tests should too — reviewed in pull requests, diffed commit by commit, greppable from your editor. TestVibe generates and runs Playwright tests in the cloud, but that doesn't mean your test files have to stay locked in a SaaS. Test Files Sync mirrors a project's test files — Gherkin features, generated Playwright specs, and configs — to a Git repository you own, and it works in both directions: edit in TestVibe and the change is committed to your repo; commit to your repo and the change flows back into TestVibe.

Here's how to set it up, what actually lands in your repo, and how the sync behaves when both sides change.

Why version-control your tests at all?

A few reasons teams turn this on:

One thing sync does not change: your project's home. TestVibe's cloud stays canonical — features, generated tests, configurations, and run history always live in your TestVibe project. The repo is a live, two-way mirror of the test files, not a migration.

Two panels, two very different connections

TestVibe has two Git settings panels, and it's worth keeping them straight:

PanelDirectionWhat it holds
Settings → App RepositoryRead-onlyYour application's own source code. TestVibe reads it to ground AI generation and map coverage.
Settings → Test Files SyncTwo-wayThis project's own test files — features, specs, Playwright configs. TestVibe writes and reads them.

They're independent, both optional, and they can even point at the same physical repository — sync stays inside its own subfolder and never writes to your app code. Connecting an App Repository does not put your test files in that repo, and Test Files Sync never gets write access to your application's source.

This guide is about the second panel.

Setting up Test Files Sync

The Test Files Sync panel before connecting: GitHub, GitLab, and Azure DevOps tabs, an owner/repository picker, and branch and folder fields with a Connect button.
The connect state: pick GitHub, GitLab, or Azure DevOps, choose the repository, and set the branch and sync folder.
  1. Open your project, then open Settings and select Test Files Sync under the Project group.
  2. Pick a provider — GitHub, GitLab, or Azure DevOps are all supported for two-way sync.
    • GitHub connects through the TestVibe GitHub App. Write access to the target repository is requested only when you enable sync — never at sign-in, which is identity-only.
    • GitLab and Azure DevOps reuse the Git connections you set up under Settings (GitLab via OAuth, Azure DevOps via a personal access token with code read & write scope).
  3. Choose the target repository from the picker.
  4. GitLab and Azure DevOps only: register the inbound webhook. The panel shows the webhook URL and a secret with copy buttons, plus per-provider instructions — paste them into your repo's webhook settings so pushes notify TestVibe immediately. GitHub handles this automatically through the App. (No webhook yet? Repo-side edits still arrive on the next sync cycle or a manual Sync now — the webhook just makes it instant.)

That's it. The first sync figures out the right seed direction automatically: if your repo's sync folder is empty, TestVibe exports the project into it; if the repo already has TestVibe content and the project is empty, it imports; if both sides have content, they're merged by identity rather than one side clobbering the other.

The Test Files Sync panel after connecting a GitHub repository: a Synced status card with last-synced and latest-commit rows, an auto-sync toggle, and Disconnect and Sync now actions.
The linked state: the connected repo card shows sync status, last-synced and latest-commit rows, the auto-sync toggle, and Sync now.

What lands in your repo

The mirror isn't a data dump — it's a runnable Playwright project, laid out under the sync folder:

testvibe/
  Features/
    Shopping/
      _group.yaml              # group metadata
      add-item-to-cart.feature # Gherkin — the readable source of truth
      add-item-to-cart.spec.js # the generated Playwright spec
  Configurations/
    Default.config.js          # browser/viewport/run settings
  package.json                 # scaffolding: run the suite locally
  playwright-runner.js
  Tools/
  README.md

Each feature group is a folder; each feature is a .feature file (the Gherkin you author) sitting next to its generated .spec.js. Every .feature carries a small identity header comment, which is how a rename shows up in Git as a move rather than a delete-plus-create.

Two scoping rules worth knowing:

This is the same canonical layout the project's Export → Download Zip produces, so a downloaded archive and a synced repo folder are interchangeable.

The feature editor in TestVibe — this Gherkin is exactly what syncs to your repo as a .feature file, with the generated spec committed alongside it.
The feature editor in TestVibe — this Gherkin is exactly what syncs to your repo as a .feature file, with the generated spec committed alongside it.

How sync behaves

TestVibe → repo (export)

Edit a feature, rename a group, regenerate a spec — shortly after the change settles, TestVibe commits it to your repository. Commits are authored by a dedicated sync bot identity, so your repo history clearly distinguishes "the team edited this in TestVibe" from human pushes, and TestVibe recognizes its own commits and never re-imports its own echo.

Repo → TestVibe (import)

Push a commit that touches the sync folder and the webhook triggers an inbound sync: changed .feature files, specs, and configs are applied to the project. Because files carry stable identities, moves and renames round-trip cleanly. Content that didn't change is left alone — the engine compares content hashes, so re-running a sync is always safe and converges to the same state.

Conflicts

Most concurrent edits never conflict: syncs are serialized per project, and each change becomes an ordered commit. A true conflict — the same feature edited on both sides between sync cycles — is handled explicitly rather than silently merged:

No line-level auto-merge guessing at intent, no last-write-wins data loss. You decide, and Git remembers everything.

A feature group in TestVibe — groups and features here map one-to-one to folders and files in the synced repo tree.
A feature group in TestVibe — groups and features here map one-to-one to folders and files in the synced repo tree.

Run it locally, too

Because the synced folder is a complete Playwright project, a teammate can clone the repo and run a spec on their own machine:

npm install
npm run runner -- --tests Features/Shopping/add-item-to-cart.spec.js

Note the bundled runner is used here, not stock npx playwright test — and specs that rely on plugin secrets still need those values, which live only in TestVibe's cloud.

Wrap-up

Test Files Sync gives you the best of both worlds: TestVibe's AI generation, cloud sandboxes, and results UI — with your test files version-controlled in the repo where your team already works. Setup is a few clicks (plus one webhook paste for GitLab/ADO), the layout is a clean, runnable Playwright project, and the conflict model is explicit and loss-free.

TestVibe is in early access — get early access to connect a repo and watch your first sync commit land. More detail lives in the docs.

Early access

Ready for tests that write themselves?