Organizing your suite
A testing framework should not force an awkward file structure on your app.
Sounding works best when your suite makes the relationship between trials, features, and layers easy to see.
Your test tree should look like the product and the testing layers, not like the framework itself.
That is why the recommended structure is intentionally boring.
A strong default structure
tests/
unit/
helpers/
e2e/
pages/
auth/
billing/
dashboard/
issues/
factories/
scenarios/This gives you three useful separations:
- by layer: unit vs page/browser
- by product surface: auth, billing, issues, dashboard
- by setup vocabulary: factories and scenarios live under
tests/
Why not tests/sounding/
Sounding should power the suite, not distort the suite.
A folder like tests/sounding/ makes the framework more visible than the product.
That is usually a smell.
The framework should disappear into the way the tests read.
Unit tests
Keep fast helper and business-logic trials under tests/unit/.
tests/unit/
helpers/
capitalize.test.js
get-user-initials.test.jsThis is where light, app-aware helper trials belong.
Page and browser trials
Put request-driven, Inertia-driven, and browser-capable trials under tests/e2e/pages/, grouped by product surface.
tests/e2e/pages/
home.test.js
blog.test.js
story.test.js
auth/
login.test.js
magic-link-request.test.js
magic-link-browser.test.js
issues/
contracts.test.js
reader-access.test.js
dashboard/
editor.test.jsThis layout works well because related behaviors stay together while different runtimes remain easy to scan.
Split by behavior, not by tool
A good file should answer this question clearly:
What family of trials lives here?
That is why names like reader-access.test.js and magic-link-browser.test.js are stronger than vague names like issues.test.js when the feature has multiple distinct behaviors.
Inside a feature folder, split files by behavior and runtime value, not by arbitrary naming.
For example:
magic-link-request.test.jsfor request/mail behaviormagic-link-browser.test.jsfor the full browser sign-in flowcontracts.test.jsfor request or Inertia-level issue contractsreader-access.test.jsfor browser-capable reading flows
That keeps the suite fast, legible, and easy to maintain.
Factories belong under tests/factories
Factories should live under tests/factories because they are test vocabulary, not app runtime code.
A practical layout might look like:
tests/factories/
user.js
issue.js
subscription.jsEach factory should model one record shape well and stay boring.
Scenarios belong under tests/scenarios
Scenarios should live under tests/scenarios because they define business situations.
tests/scenarios/
issue-access.js
publisher-editor.js
magic-link-signin.jsA good scenario name should tell you the situation before you open the file.
A useful rule of thumb
If a trial reads better with a named business situation, add or reuse a scenario.
If a trial just needs one more field value, prefer:
- a trait
- a small override
- a helper inside the scenario
Do not create a new scenario for every tiny variation.
Keep the feature folders calm
A few practical rules help a lot:
- keep one file focused on one kind of behavior
- prefer
contracts.test.jsfor request/Inertia contracts - prefer
*-browser.test.jsfor true browser journeys - group by feature before grouping by runtime detail
- do not let one giant file become the whole feature
A real-app shape that works well
This is the kind of layout Sounding is designed to support in a real app:
tests/
unit/
helpers/
capitalize.test.js
get-user-initials.test.js
e2e/
pages/
auth/
login.test.js
magic-link-request.test.js
magic-link-browser.test.js
billing/
pricing.test.js
dashboard/
editor.test.js
issues/
contracts.test.js
reader-access.test.js
home.test.js
blog.test.js
contact.test.js
story.test.js
scenarios/
issue-access.js
publisher-editor.jsThat structure is simple, scalable, and easy to onboard someone into.
The main idea
A Sounding suite should look like a well-organized product codebase that happens to use Sounding.
Not like a product codebase that has been bent around the framework.