Loading...

Blog

login.spec.ts craft-test core automation framework site-a-testsconsumer framework site-b-testsconsumer framework site-c-testsconsumer framework Fixed once. Fixed everywhere.

Stop Duplicating Your Tests: craft-test, a Shared Core Framework for Scalable Test Automation

A single core automation framework, lightweight consumer frameworks, and every fix made exactly once.

Most test automation suites begin the same way.

A team writes an automated login test: a script that opens the website, enters a username and password, and confirms the user is signed in. When another product needs the same coverage, the test is duplicated, and then duplicated again for the next one.

Months later, the login button changes. The same fix now has to be applied in every single copy, often by different people. Over time the copies drift apart, each fix lands slightly differently, and defects slip through the gaps.

craft-test was designed to remove that duplication entirely. Built with Playwright, a tool that drives a web browser automatically, and TypeScript, it centralises everything that can be shared and keeps each product’s test setup deliberately small.

The core idea: one core, many consumers

Think of a restaurant chain

Individual branches don’t write their own recipes. The head office maintains one master recipe book, and every branch works from it. Each branch contributes only its local details: its address, its menu and its opening hours. When the head chef refines a recipe, every branch serves the improved dish immediately.

craft-test follows the same model and is made up of two kinds of framework:

The core automation framework

The master recipe book. It owns the shared tools, the shared tests and the configuration, and is packaged so any test project can install it as a dependency.

The consumer test automation frameworks

The branches. Each product under test has its own lightweight framework that installs and uses the core. It contains no shared logic, only what is unique to its website: a description of the site, its test users, and a single instruction naming the shared tests to run.

Dependencies flow in one direction only: consumers rely on the core, never the reverse. As a result, a fix made in the core reaches every consumer with its next update, and bringing a new product under test means adding one lightweight consumer framework rather than cloning an existing project.

In plain wordsAll the heavy lifting happens in one core framework. Each product’s consumer framework simply connects to it and describes its own website.

What’s inside the core

The core is organised into clearly defined layers, each with a single responsibility:

helpersReusable building blocks for clicking, typing, waiting, managing browser tabs, generating realistic test data, and reading CSV, Excel, Word, PDF and ZIP files, along with a logger that masks passwords automatically.
assertionsThe verification layer. It confirms that what appears on screen matches what is expected, waiting for the page to settle before reaching a verdict.
pagesDefine how to interact with each page, but never verify outcomes. Verification is left entirely to the assertions layer.
featuresA dedicated folder for each page or component, such as login.
fixturesResources a test can request by name, such as a login page or a test user, prepared before the test runs and cleaned up once it completes.
setupSigns in through the back end ahead of time, so tests that aren’t about login start already authenticated.
reportingLabels every step with the element involved and the exact line in the test, and attaches screenshots and a run summary to any failure.
configA single shared configuration. Before any test runs, it confirms the website is reachable and stops within seconds with one clear message if it isn’t.

One feature, one folder

In large test suites, finding the right file can take longer than making the change. craft-test avoids this by keeping everything related to a feature together. Any change to login, whether a locator, a label, an action or a test, happens in one folder:

features/login/
├── web/
│   ├── pages/    login.selector.ts   where the elements are
│   │             login.content.ts    what the page says
│   │             login.page.ts       what it can do
│   └── specs/    login.spec.ts       what must be true (the tests)
└── index.ts

One design decision makes this structure especially powerful. The login page does not hard-code its buttons and labels; they are supplied from separate files. This allows each consumer framework to specify where elements sit on its own website without any change to the page itself.

In plain wordsThe core knows how to log in. Each consumer tells it where things are on its website.

A consumer framework is mostly configuration

Because the core carries the logic, a consumer framework needs remarkably little code. This is its entire test file:

import { loginPositiveTests } from 'craft-test';
import { test } from '../fixtures/fixtures';

loginPositiveTests(test);

The first line imports the shared login tests from the installed core. The last line runs them against the consumer’s own website. Everything else in the consumer is configuration rather than code.

Each consumer maintains a concise site profile describing its brand, language and region, the user roles that exist, and the address of each environment, such as staging and production.

The profile also captures anything unusual about the site. If the login page lives at an unexpected address or its fields are labelled differently, the consumer records that detail and the core adapts accordingly. The consumer never writes login code of its own.

In plain wordsRather than teaching the framework a new routine for every website, each consumer simply hands the core an updated map.

Security by design

Test accounts still use real credentials, so the framework enforces three safeguards:

1
Credentials are test dataThey are stored in a private file for each environment and never committed to shared code. Only a template is shared.
2
Passwords are always maskedEvery password is masked as soon as it is read, appearing as ****** in all logs, steps and reports.
3
CI-ready secretsAutomated pipelines supply credentials through secure secrets, so they never depend on the private file.

The principles behind it

Three design principles shape the entire framework:

1

The core executes, consumers describe. Pages, actions and tests exist only in the core. A consumer framework never rebuilds them; it only describes its website.

2

Configuration over code. Differences between websites are expressed in a site profile, not in new code, which keeps every consumer small, readable and easy to maintain.

3

Clear separation of concerns. Test data, machine settings and test logic each live in their own place, so changing one never puts the others at risk.

Built to scale

  • New features, one folder. Adding shared coverage such as search, contact forms, navigation or accessibility checks means adding a single folder to the core, complete with its own tests.
  • Every consumer benefits. New features and fixes reach a consumer framework as soon as it installs the latest version of the core package.
  • Ready for more platforms. The feature-based structure already leaves room for mobile and API testing alongside web.
The key takeaway

One core. Many consumers.
All the shared code lives in the core; each consumer just describes its website.
Fixed once, fixed everywhere.

Authored By:

Sanchi
Automation QA engineering, Span Technoverse Sanchi, SDET at Span Technoverse