Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## 0.9.7 — 2026-09-15

- Update Wesper to 0.4.1, retaining compatibility with optional ownership and taxonomy evidence and partial collection warnings.
- Reuse WordPress between sequential proof cases while retaining each artifact installation and browser check.
- Run focused CI checks for allowlisted documentation and demo updates pushed to main.

## 0.9.6 — 2026-09-14

- Point the package homepage to the Block Runner website on Accelerate.
Expand Down
49 changes: 45 additions & 4 deletions dev/test/context.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,44 @@
import { afterEach, describe, expect, it, vi } from 'vitest';
import type { SiteContext } from 'wesper';

const collect = vi.fn(async () => ({ contextVersion: 1 }));
const stringifyManifest = vi.fn(() => '{"ok":true}\n');
const collectedContext = {
$schema: 'https://humanmade.github.io/wesper/schemas/site-context-v1.schema.json',
contextVersion: 1,
site: { environment: 'local', isMultisite: false },
provenance: {
collectedAt: '2026-09-15T00:00:00.000Z',
collector: 'wp-cli',
collectorVersion: '0.2.3',
sourceHash: 'sha256:fixture',
partial: true,
},
blocks: {
types: [{
name: 'acme/catalog',
attributes: {},
supports: {},
source: 'plugin',
owner: { status: 'unknown', reason: 'scan_incomplete' },
}],
},
contentModel: {
postTypes: [{
name: 'product',
taxonomies: ['product_cat'],
fields: [],
owner: { status: 'unknown', reason: 'incomplete_registration_trace' },
}],
taxonomies: [{
name: 'product_cat',
objectTypes: ['product'],
owner: { status: 'unknown', reason: 'incomplete_registration_trace' },
}],
},
warnings: [],
} satisfies SiteContext;

const collect = vi.fn(async () => collectedContext);
const stringifyManifest = vi.fn((_context: SiteContext) => '{"ok":true}\n');

vi.mock('wesper', () => ({ collect, stringifyManifest }));

Expand Down Expand Up @@ -44,10 +81,14 @@ describe('collectSiteContext', () => {
);
});

it('returns the stringified manifest', async () => {
it('passes newer optional ownership and taxonomy associations to Wesper unchanged', async () => {
const result = await collectSiteContext({});

expect(stringifyManifest).toHaveBeenCalledWith({ contextVersion: 1 });
expect(stringifyManifest.mock.calls[0]?.[0]).toBe(collectedContext);
expect(collectedContext.contentModel).toMatchObject({
postTypes: [{ name: 'product', taxonomies: ['product_cat'], owner: { status: 'unknown' } }],
taxonomies: [{ name: 'product_cat', objectTypes: ['product'], owner: { status: 'unknown' } }],
});
expect(result).toBe('{"ok":true}\n');
});
});
60 changes: 60 additions & 0 deletions dev/test/styles.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { mkdtempSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import path from 'node:path';
import { describe, expect, it } from 'vitest';
import { convert } from '../../src/index.js';
import type { RuleContext } from '../../src/types.js';
Expand All @@ -7,6 +10,13 @@ import { expandShorthand, parseInlineStyle } from '../../src/styles/parse.js';
const warningsOf = (items: Awaited<ReturnType<typeof convert>>['items']) =>
items.filter((item) => item.status === 'warning').map((item) => item.reason);

function writeContext(manifest: unknown): string {
const directory = mkdtempSync(path.join(tmpdir(), 'block-runner-context-'));
const contextPath = path.join(directory, 'site.context.json');
writeFileSync(contextPath, JSON.stringify(manifest));
return contextPath;
}

describe('inline style parsing', () => {
it('expands box shorthands with CSS 1-4 value semantics', () => {
expect(expandShorthand('padding', '10px')).toEqual([
Expand Down Expand Up @@ -904,6 +914,56 @@ describe('styling — capability source', () => {
expect(report.output).toContain('padding-top:8px');
});

it('does not block conversion for additive collector relationships and a partial block warning', async () => {
const context = writeContext({
contextVersion: 1,
site: { environment: 'local', isMultisite: false },
provenance: {
collectedAt: '2026-09-15T00:00:00.000Z',
collector: 'wp-cli',
collectorVersion: '0.2.3',
sourceHash: 'sha256:fixture',
partial: true,
},
blocks: {
types: [{
name: 'core/group',
attributes: {},
supports: { spacing: { padding: true } },
source: 'core',
owner: { status: 'unknown', reason: 'scan_incomplete' },
providesContext: { 'acme/productId': 'productId' },
}],
},
contentModel: {
postTypes: [{
name: 'product',
taxonomies: ['product_cat'],
owner: { status: 'unknown', reason: 'incomplete_registration_trace' },
}],
taxonomies: [{
name: 'product_cat',
objectTypes: ['product'],
owner: { status: 'unknown', reason: 'incomplete_registration_trace' },
}],
},
warnings: [
{
code: 'blocks.provides_context_attribute_missing',
severity: 'warning',
surface: 'blocks.types.core/group.providesContext.acme/productId',
message: 'Block "core/group" provides context "acme/productId" from "productId", but the registered block attributes do not include it.',
coverage: 'partial',
},
],
});

const report = await convert(`<div style="padding:8px">Text</div>`, { context });

expect(report.ok).toBe(true);
expect(report.output).toContain('padding-top:8px');
});

it('fails rather than widening back to the pin when an explicit context is unreadable', async () => {
const report = await convert(`<div style="padding:8px">Text</div>`, {
context: 'dev/test/fixtures/does-not-exist.json',
Expand Down
12 changes: 6 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "block-runner",
"version": "0.9.6",
"version": "0.9.7",
"description": "The layer between generated content and WordPress \u2014 convert what AI, agents, and design tools emit into clean, valid, native Gutenberg blocks, and validate every result. CLI + library.",
"type": "module",
"license": "GPL-2.0-or-later",
Expand Down Expand Up @@ -102,7 +102,7 @@
"postcss": "8.5.26",
"react": "18.3.1",
"react-dom": "18.3.1",
"wesper": "0.0.3"
"wesper": "0.4.1"
},
"devDependencies": {
"@playwright/test": "1.61.1",
Expand Down
Loading