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
56 changes: 56 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ jobs:
- uses: actions/checkout@v5
with:
persist-credentials: false
with:
persist-credentials: false

- name: Setup pnpm
uses: pnpm/action-setup@v5
Expand Down Expand Up @@ -157,3 +159,57 @@ jobs:

# Clean up server
kill "$SERVER_PID" || true

production-browser:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v5
Comment thread
coderabbitai[bot] marked this conversation as resolved.
with:
persist-credentials: false

- name: Setup pnpm
uses: pnpm/action-setup@v5
with:
run_install: false

- uses: actions/setup-node@v5
with:
node-version: '24.14.1'
cache: 'pnpm'

- uses: astral-sh/setup-uv@v7
with:
version: "latest"

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Cache generated production browser fixtures
id: cache-production-browser-fixtures
uses: actions/cache@v5
with:
path: test-fixtures-production-browser
key: ${{ runner.os }}-production-browser-fixtures-0.7.2-${{ hashFiles('python/scripts/generate_fixtures.py', 'python/v0.7.2/**') }}

- name: Generate test fixtures (Python spatialdata 0.7.2)
if: steps.cache-production-browser-fixtures.outputs.cache-hit != 'true'
run: pnpm test:fixtures:generate:0.7.2 -- --output-dir test-fixtures-production-browser

- name: Expose production browser fixtures
run: ln -s test-fixtures-production-browser test-fixtures

- name: Install Chromium and Linux dependencies
run: pnpm exec playwright install --with-deps chromium

- name: Build and browser-test the production browser consumer
run: pnpm test:browser:production

- name: Upload production browser artifacts
if: always()
uses: actions/upload-artifact@v5
with:
name: production-browser-artifacts
path: test-results
if-no-files-found: ignore
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ packages/zarrextra/src/*.d.ts.map
# Testing
coverage/
test-fixtures/
test-fixtures-production-browser/
test-results/
validation-results/

# Python virtual environments (version-specific, completely separate)
Expand Down
10 changes: 6 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"test": "pnpm -r --filter='!docs' run test",
"test:unit": "vitest run --exclude tests/integration/**",
"test:integration": "vitest run tests/integration",
"test:browser:production": "pnpm build && pnpm exec vite build --config tests/production/browser/vite.config.ts && pnpm exec playwright test --config tests/production/browser/playwright.config.ts",
"test:all": "pnpm test:unit && pnpm test:integration",
"test:fixtures:generate": "uv run python/scripts/generate_fixtures.py",
"test:fixtures:generate:0.5.0": "uv run python/scripts/generate_fixtures.py --version 0.5.0",
Expand Down Expand Up @@ -44,13 +45,14 @@
"@biomejs/biome": "^2.5.3",
"@changesets/changelog-github": "^0.7.0",
"@changesets/cli": "^2.31.0",
"@typescript-eslint/parser": "^8.62.0",
"eslint": "^10.6.0",
"eslint-plugin-react-hooks": "^7.1.1",
"@playwright/test": "^1.62.0",
"@rolldown/plugin-babel": "catalog:",
"@typescript-eslint/parser": "^8.62.0",
"@vitejs/plugin-react": "catalog:",
"babel-plugin-react-compiler": "catalog:",
"@zarrita/storage": "catalog:",
"babel-plugin-react-compiler": "catalog:",
"eslint": "^10.6.0",
"eslint-plugin-react-hooks": "^7.1.1",
"typescript": "catalog:",
"vite": "catalog:",
"vitest": "catalog:"
Expand Down
5 changes: 4 additions & 1 deletion packages/layers/src/FlatPolygonLayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,10 @@ export class FlatPolygonLayer extends (Layer as any) {
}): void {
super.updateState(params);
const { props, oldProps, changeFlags } = params;
if (changeFlags.extensionsChanged) {
// Deck marks extensionsChanged on the update immediately following
// initializeState. `oldProps.extensions` is absent in that first update, so
// the model created during initialization is already current.
if (changeFlags.extensionsChanged && oldProps.extensions !== undefined) {
this.state.model?.destroy();
this.state.model = this._getModel();
}
Expand Down
67 changes: 55 additions & 12 deletions packages/layers/tests/flatPolygonLayer.spec.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,77 @@
import { describe, expect, it, vi } from 'vitest';
import { beforeEach, describe, expect, it, vi } from 'vitest';

const modelOptions = vi.hoisted((): Array<{ shaderAssembler?: unknown }> => []);
const modelInstances = vi.hoisted((): Array<{ destroy: ReturnType<typeof vi.fn> }> => []);

vi.mock('@luma.gl/engine', () => ({
Model: class {
destroy = vi.fn();

constructor(_device: unknown, options: { shaderAssembler?: unknown }) {
modelOptions.push(options);
modelInstances.push(this);
}
},
}));

import { FlatPolygonLayer } from '../src/FlatPolygonLayer';

function makeProps() {
return {
id: 'flat-polygon-shader-modules',
ringPositions: new Float32Array([0, 0, 1, 0, 0, 1]),
ringVertexCount: 3,
triangleData: new Uint32Array([0, 1, 2, 7]),
triangleCount: 1,
featureColors: new Uint8Array([0, 0, 0, 255]),
featureCount: 1,
featureScale: new Float32Array([1]),
};
}

describe('FlatPolygonLayer shaders', () => {
it('uses Deck’s shader assembler when creating its hand-rolled Model', () => {
const layer = new FlatPolygonLayer({
id: 'flat-polygon-shader-modules',
ringPositions: new Float32Array([0, 0, 1, 0, 0, 1]),
ringVertexCount: 3,
triangleData: new Uint32Array([0, 1, 2, 7]),
triangleCount: 1,
featureColors: new Uint8Array([0, 0, 0, 255]),
featureCount: 1,
featureScale: new Float32Array([1]),
});
beforeEach(() => {
modelOptions.length = 0;
modelInstances.length = 0;
});

it("uses Deck's shader assembler when creating its hand-rolled Model", () => {
const layer = new FlatPolygonLayer(makeProps());
const shaderAssembler = {};
layer.context = { defaultShaderModules: [], shaderAssembler };

layer._getModel();

expect(modelOptions.at(-1)).toMatchObject({ shaderAssembler });
});

it("does not recreate the model for Deck's first forced extensionsChanged update", () => {
const props = makeProps();
const layer = new FlatPolygonLayer(props);
const initialModel = { destroy: vi.fn() };
layer.state = { model: initialModel };
layer.context = { defaultShaderModules: [], shaderAssembler: {}, device: {} };

layer.updateState({ props, oldProps: { ...props }, changeFlags: { extensionsChanged: true } });

expect(initialModel.destroy).not.toHaveBeenCalled();
expect(modelOptions).toHaveLength(0);
});

it('recreates the model for later extension changes', () => {
const props = makeProps();
const layer = new FlatPolygonLayer(props);
const initialModel = { destroy: vi.fn() };
layer.state = { model: initialModel };
layer.context = { defaultShaderModules: [], shaderAssembler: {}, device: {} };

layer.updateState({
props,
oldProps: { ...props, extensions: [] },
changeFlags: { extensionsChanged: true },
});

expect(initialModel.destroy).toHaveBeenCalledOnce();
expect(modelInstances).toHaveLength(1);
});
});
38 changes: 38 additions & 0 deletions pnpm-lock.yaml

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

12 changes: 12 additions & 0 deletions tests/production/browser/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Production layer browser smoke test</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src.tsx"></script>
</body>
</html>
31 changes: 31 additions & 0 deletions tests/production/browser/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { defineConfig } from '@playwright/test';

export default defineConfig({
testDir: '.',
testMatch: '*.spec.ts',
fullyParallel: false,
workers: 1,
use: {
baseURL: 'http://127.0.0.1:4173',
browserName: 'chromium',
headless: true,
// CI has no hardware GPU. Chromium's SwiftShader ANGLE path still exposes
// WebGL2, which the polygon-shapes scenario probes before compiling the
// FlatPolygonLayer shader.
launchOptions: { args: ['--use-gl=angle', '--use-angle=swiftshader'] },
},
webServer: [
{
command: 'pnpm test:server',
port: 38473,
reuseExistingServer: !process.env.CI,
timeout: 30_000,
},
{
command: 'pnpm exec vite preview --config vite.config.ts',
port: 4173,
reuseExistingServer: !process.env.CI,
timeout: 30_000,
},
],
});
Loading