Skip to main content
Version: v1.0.0

Adding Projects

Status: Stable

This guide explains how to add new projects to an existing @qs/design-tests configuration.

Adding a New Project​

Edit your .designTests.js configuration to add a new project:

module.exports = {
projects: {
// Existing project
webapp: {
key: "webapp",
name: "Web Application",
url: "http://localhost:6006",
},

// NEW PROJECT
mobile: {
key: "mobile",
name: "Mobile Application",
url: "http://localhost:6007",
// Optional: different viewports for mobile
viewports: [
{ name: "mobile", width: 375, height: 812 },
],
}
},

// Global viewports (used by projects that don't specify their own)
viewports: [
{ name: "desktop", width: 1440, height: 900 },
],
};

Run Tests​

Run the tests - every test of the new project is reported as NEW, while the existing project compares against its baseline:

pnpm exec design-tests run --config .designTests.js --all

Output:

📸 ✅  OK  [#1/195] desktop components/button "Primary" (1830ms)
📸 🌱 NEW! [#2/195] mobile pages/home "Home" (2102ms)
...
✅ Tests complete: 150 passed, 45 new (5m 02s)

Promote the new project's screenshots into the baseline and commit them:

pnpm exec design-tests baseline-update --config .designTests.js --project mobile
git add design-tests/baseline-images/
git commit -m "Add design tests baseline for mobile"

Directory Structure​

The baseline images are organized by project:

design-tests/
├── baseline-images/
│ ├── webapp/ # Existing project
│ │ └── components/
│ └── mobile/ # NEW - automatically created
│ └── screens/
├── actual-images/
├── diff-output/
└── report/

Commit New Baseline​

Commit only the new project's baseline:

git add design-tests/baseline-images/mobile/
git commit -m "Add baseline for mobile project"
git push

Test a Single Project​

To test only the new project during setup:

npx design-tests run --config .designTests.js --project mobile

This is useful for:

  • Faster iteration during project setup
  • Debugging project-specific configuration
  • Testing URL accessibility

Multi-Project Example​

Here's an example with multiple projects (like a suite of 8 Storybooks):

module.exports = {
projects: {
components: {
key: "components",
url: "http://localhost:6001",
ignoreList: ["internal/*"],
},
widgets: {
key: "widgets",
url: "http://localhost:6002",
},
templates: {
key: "templates",
url: "http://localhost:6003",
},
// ... up to 25+ projects
},

viewports: [
{ name: "mobile", width: 375, height: 812 },
{ name: "desktop", width: 1440, height: 900 },
],
};

Same Storybook, Multiple Tenants (storyParams)​

Some Storybooks render several tenant themes from one deployment, selected by a query parameter rather than by URL. A component Storybook serves eight tenants this way, picked with ?theme=Banana. storyParams appends a project's query parameters to every story URL it builds, so each tenant becomes its own project — with its own baseline — against the same url. Generate the eight blocks from a tenant array instead of pasting eight near-identical objects:

const tenants = ["Apple", "Banana", "Cherry", "Fig", "Grape", "Lemon", "Mango", "Pear"];

module.exports = {
projects: Object.fromEntries(
tenants.map((tenant) => {
const key = tenant.toLowerCase();
return [
key,
{
key,
name: `Component Storybook (${tenant})`,
url: "http://storybook.internal.example",
publicUrl: "https://storybook.example.com",
storyParams: { theme: tenant },
},
];
})
),

viewports: [
{ name: "mobile", width: 375, height: 812 },
{ name: "desktop", width: 1440, height: 900 },
],
};

This produces capture URLs like http://storybook.internal.example/iframe.html?id=basis-base-colors--base-colors&hideAnimation=true&theme=Banana and matching public links with &theme=Banana appended, so the HTML report links to the exact tenant rendering that was captured. A project with no storyParams is unaffected — its URLs are unchanged. The same mechanism covers other query-driven variants, such as language or grid.

A story that pins its own variant overrides the query parameter. Storybook reads parameters before the URL, so a story declaring parameters: { theme: "Cherry" } renders Cherry in every project and produces one identical image per project. That is redundant rather than wrong — exclude such stories per project with ignoreList when the duplicate captures cost more than the config does.

Per-Project Configuration​

Each project can override global settings:

projects: {
slowapp: {
key: "slowapp",
url: "http://localhost:8080",

// Override stability settings for this project
stability: {
strategy: "advanced",
waitForNetworkIdle: true,
networkIdleTimeout: 60000, // Longer timeout
waitBeforeScreenshot: 2000,
},

// Custom screenshot path builder
snapshotsDirFn: ({ projectKey, story, viewport }) => {
return `${projectKey}/${story.title}/${story.name}-${viewport.name}`;
},

// Project-specific viewports
viewports: [
{ name: "ultra-wide", width: 2560, height: 1440 },
],
}
}

Testing Non-Storybook Pages​

For projects without Storybook (like a CMS site), use manual page lists:

projects: {
cms: {
key: "cms",
name: "CMS Pages",
url: "http://www.example.com",

// Manual page list instead of Storybook discovery
pages: [
{ name: "Homepage/DE", path: "/de/" },
{ name: "Homepage/EN", path: "/en/" },
{ name: "Products/Listing", path: "/en/products" },
{ name: "Contact", url: "http://contact.example.com/form" }, // Absolute URL
],
}
}

Removing a Project​

To remove a project:

  1. Delete it from .designTests.js
  2. Delete its baseline directory
  3. Commit the changes
rm -rf design-tests/baseline-images/old-project/
git add .designTests.js design-tests/baseline-images/
git commit -m "Remove old-project from design tests"

Next Steps​