nu-sl2t
v1.0.0
Published
nuances' Liquid Testing Tool
Readme
Liquid Testing Tool
Render Shopify Liquid sections/snippets in a real browser and test them with Playwright. Your theme’s JS (TypeScript) and CSS (Tailwind) are loaded globally so components behave like in production.
- Liquid engine: LiquidJS (Shopify compatible) — see
harttle/liquidjs
Requirements
- Node 20+
- Bun (to run the dev server commands)
- Playwright browsers (first run):
npx playwright install
Install (in your Shopify theme)
pnpm add -D liquid-testing-tool @playwright/test
# one-time (per machine)
npx playwright install
# recommended runtime for the dev server (optional but suggested)
brew install oven-sh/bun/bunConfigure
Create liquidtest.config.mjs in your repo:
export default {
entries: {
css: ['/assets/main.css'],
js: ['/assets/main.ts']
},
theme: {
// optional; defaults to [process.cwd()]
// roots: [process.cwd()]
},
server: {
port: 5175,
useVite: true, // JIT TS + Tailwind via Vite middleware
rootContainerSelector: '#__liquidtest-root'
}
};Commands
- Start dev server and browse manually:
liquidtest dev ./liquidtest.config.mjs
# open http://localhost:5175- Or via Playwright (webServer) it will auto-start (see below).
- Run E2E tests:
pnpm test # headless
pnpm test:e2e:headed
pnpm test:e2e:ui # Playwright UI modeWriting tests
Encode a render payload into a single GET param p (base64url JSON). Use the helper to do this for you.
Example (Playwright):
// tests/utils.ts
import type { Locator, Page } from '@playwright/test';
import type { LiquidRenderPayload } from 'liquid-testing-tool';
export async function prepareLiquidTest(page: Page, input: LiquidRenderPayload) {
const payload: LiquidRenderPayload = 'component' in input
? { component: input.component, context: input.context }
: { components: input.components, context: input.context };
const json = JSON.stringify(payload);
const b64 = Buffer.from(json).toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
const url = `/render?p=${b64}`;
await page.goto(url);
const testContainer: Locator = page.locator('#__liquidtest-root');
return { testContainer };
}
// tests/render.spec.ts
import { test, expect } from '@playwright/test';
import { prepareLiquidTest } from './utils';
test('render single component', async ({ page }) => {
const { testContainer } = await prepareLiquidTest(page, {
component: 'sections/hello.liquid',
context: { section: { settings: { heading: 'Hello' } } },
});
await expect(testContainer.locator('.title')).toHaveText(/Hello/);
});
test('render multiple components in order', async ({ page }) => {
const { testContainer } = await prepareLiquidTest(page, {
components: [
{ component: 'sections/hello.liquid', context: { section: { settings: { heading: 'First' } } } },
{ component: 'sections/hello.liquid', context: { section: { settings: { heading: 'Second' } } } },
],
context: { globals: { locale: 'en' } },
});
await expect(testContainer.locator('.title')).toHaveCount(2);
});How it works
- Dev server exposes
/render?p=.... - Payload supports either
{ component, context }or{ components: [...], context }. - Context is merged: global
contextthen per-itemcontext. - HTML shell is in
src/server/shell.html; CSS/JS entries are injected for every page.
Web components / external JS
- With
useVite: true, TypeScript is JIT-transpiled and Tailwind runs via your local config. - Your
main.tsregisters web components and third-party libs; they’re available in tests and when browsing pages.
Playwright integration (webServer)
Add playwright.config.ts to auto-start the server for tests.
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests-e2e',
use: { baseURL: 'http://localhost:5175' },
projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],
webServer: {
command: 'liquidtest dev ./liquidtest.config.mjs',
url: 'http://localhost:5175/health',
reuseExistingServer: true,
timeout: 30000
}
});Roadmap / Notes
- Optional HTML normalization (serializer) for stable text/snapshot assertions.
- Broader Shopify Liquid coverage (image filters, URL helpers, money, etc.).
- Visual snapshot testing with Playwright (optional).
Reference
- LiquidJS:
harttle/liquidjs
