Tagixo Docs

Developer Documentation for Laravel, SDK integrations, and extensibility

Operations

Testing and E2E

Three-layer test suite (Vitest, Playwright, Pest) and the testing-routes harness for E2E coverage.

Testing and E2E

Tagixo ships with a three-layer test suite — about 2,300 tests in total — covering everything from drawer-click reactivity to public-route rendering and PDF generation. As a consumer, this means you do NOT have to re-test the renderer, persistence round-trip, or snapshot regression in your own application. You test your own glue code.

The three layers

Layer Tool Tests Proves
Unit / component Vitest + Vue Test Utils ~1700 Drawer UI mutations, composables, canvas components, runtime JS for accordion / tabs / slider / popup / sticky
End-to-end matrix Playwright ~340 (cases × 3 browsers + snapshots) Real browser: store mutation → renderer → DOM, with computed-style and attribute assertions
Integration Pest PHP ~170 Renderer contracts, persistence round-trip, public route chain, PDF generation
Feature / Unit (PHP) Pest PHP ~120 Controllers, services, models, PropTypes, modules

E2E harness

The Playwright suite runs against a dedicated testing harness mounted at /tagixo-test/*. The harness is gated by two conditions:

  1. config('tagixo.testing_routes') === true
  2. app()->environment() !== 'production'

The standard way to flip the flag is the TAGIXO_TESTING_ROUTES=true environment variable. The package's playwright.config.js injects it into the dedicated dev server (port 8001, kept separate from your regular php artisan serve on 8000) so a parallel developer workflow never breaks tests.

Do NOT enable testing routes in production. They disable CSRF on the harness group and expose write endpoints intended for Playwright APIRequestContext, not browser cookies.

Matrix-driven Playwright

The E2E suite is fixture-driven. Each context has blank.json and basic.json fixtures, and a single matrix/cases.js file enumerates ~80 cases. A case looks like:

{
    name: 'page · section · background-color #ff0000',
    context: 'page',
    setup:  { components: [...], body: {} },
    mutate: {
        id: 'sec-id',
        patch: { background: { colorEnabled: true, color: '#ff0000' } },
    },
    assertions: [
        { type: 'attached', selector: '.vb-section' },
        {
            type: 'computed-style',
            selector: '.vb-section',
            property: 'background-color',
            expected: 'rgb(255, 0, 0)',
        },
    ],
    preview: { containerWidth: 600 },  // optional — for @container queries
}

Adding a new test = adding a row. The runner emits one test() per row and sweeps it across chromium / firefox / webkit automatically.

Snapshot workflow

specs/snapshots.spec.js writes one .txt baseline per case under matrix/__snapshots__/ containing the renderer's HTML+CSS output. Subsequent runs compare byte-exactly. Snapshots run only on chromium because server-rendered output is browser-agnostic — duplicating across firefox/webkit only adds noise.

After an intentional renderer change, refresh baselines:

cd tagixo && UPDATE_SNAPSHOTS=1 npm run test:e2e -- --project=chromium specs/snapshots.spec.js

Coverage matrix

Context Areas
page section bg / spacing / border / box-shadow / custom-css / transform / filter / heading levels / button / image / text / spacer / icon / gallery / accordion / tabs / social-icons / responsive (@container queries) / sizing / animation / visibility / nested hierarchy
form text-input / textarea / select (+multiple) / checkbox / radio (+inline) / date-picker / file-upload / submit-button / grid (cols+gap) / fieldset / group / section / wizard / tabs
mail section bg / padding / border-radius / box-shadow / heading level / button / text / nested hierarchy
pdf section bg / padding / border-radius / box-shadow / heading level / button / text / nested hierarchy

PHP integration suite

Located under tagixo/tests/Integration/. Highlights:

  • Persistence round-trip — Save → DB → reload → render produces identical HTML for empty / single / nested / multi-PropType / responsive / multi-section payloads
  • Public route chain — Save → DB cache → public route → response, end-to-end
  • Mail/PDF rendering — scaffold correctness, inlined CSS, @page rules, paper sizes, preheader, subject/title
  • PDF generation — pipes PdfRenderer output through dompdf/dompdf and extracts text with smalot/pdfparser (auto-skipped when dompdf isn't installed)
  • Cache invalidationPage::invalidateCache() and save hooks

You can run them from your project root once the package is installed in dev:

vendor/bin/pest tagixo/tests/Integration/

Building your own PDF tests

If you wire a different PDF engine (Browsershot, Snappy), mirror the dompdf integration test:

$bytes = \Spatie\Browsershot\Browsershot::html($pdfTemplate->renderHtml())->pdf();
expect(substr($bytes, 0, 5))->toBe('%PDF-');

That's enough to verify the round-trip without depending on the package's optional dompdf dev dependency.

Commands cheat-sheet

# JS tests (Vitest) — run inside the package directory
cd tagixo && npm run test:run
cd tagixo && npm test                  # watch mode
cd tagixo && npm run test:coverage

# E2E tests (Playwright)
cd tagixo && npm run test:e2e                       # all browsers
cd tagixo && npm run test:e2e -- --project=chromium # one browser
cd tagixo && npm run test:e2e:ui                    # interactive

# PHP tests (Pest) — run from project root
vendor/bin/pest
vendor/bin/pest tagixo/tests/Integration/
vendor/bin/pest --filter="PersistenceRoundTrip"

# First-time E2E setup
cd tagixo && npm run test:e2e:install               # install chromium/firefox/webkit
php artisan vendor:publish --tag=tagixo-assets --force

What you don't need to re-test

The package suite already proves renderer correctness, persistence round-trip, public route chain, and snapshot regression across all four contexts. Consumer apps typically only need to test:

  • Custom modules you write
  • Custom controllers or BuilderTypeContract handlers
  • Business logic on top of the builder JSON (publishing rules, approval flows)
  • Custom PropTypes and Props (if you wire CSS generation, run your own round-trip)

Leave the renderer alone — the package guarantees it.