Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
d56466a
Add Playwright E2E test suite for FW Lite
myieye May 7, 2026
87fe0ec
Fix lint
myieye May 8, 2026
cdda9a8
Cleanup
myieye May 8, 2026
e2e7290
Maybe cleanup?
myieye May 8, 2026
1c49043
Stabilize FW Lite e2e tests + add CI concurrency cancellation
myieye May 8, 2026
5747ecb
Refactor
myieye May 11, 2026
d02c227
Rename "integration" to "launcher"
myieye May 11, 2026
3c30769
Remove unused shadcnMode
myieye May 11, 2026
c66f697
Fix: restore onMount import in ShadcnProjectView
myieye May 11, 2026
8c9969c
Refactor FwLite e2e + launcher tests for readability
myieye May 11, 2026
d5e060c
ci: always install Playwright system deps in fw-lite workflow (#2293)
myieye May 22, 2026
d2237fe
Merge remote-tracking branch 'origin/develop' into e2e-test-fw-lite
myieye Jun 17, 2026
b660e6a
Polish e2e branch after develop merge
myieye Jun 17, 2026
ca591be
Address Devin findings: pnpm action version + stale task name
myieye Jun 17, 2026
627bea9
Merge remote-tracking branch 'origin/develop' into e2e-test-fw-lite
myieye Jul 1, 2026
a2439d1
Tidy e2e branch: drop dead config, trim comments, restore CI concurrency
myieye Jul 1, 2026
1a08446
Fix seeded project codes in e2e SEEDED_PROJECT_IDS map
myieye Jul 1, 2026
f1bc46d
Address CodeRabbit review: harden e2e launcher/fixture reliability
myieye Jul 2, 2026
c21d3d2
Merge branch 'develop' into e2e-test-fw-lite
myieye Jul 3, 2026
2d60265
Add argos screenshots
myieye Jul 3, 2026
f2939af
Specify GHA permissions
myieye Jul 3, 2026
e86a14d
Give the e2e Argos upload its own build name
myieye Jul 6, 2026
a3a6918
Screenshot the entry editor in the project-download e2e test
myieye Jul 6, 2026
3397191
Take e2e Argos screenshots in light and dark mode
myieye Jul 6, 2026
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
164 changes: 160 additions & 4 deletions .github/workflows/fw-lite.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ on:
- develop
- main
- feat/sync-morph-types #just for now ensure PRs to this branch have checks run

# Only cancel superseded PR runs; never cancel release builds triggered by a push.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

permissions:
contents: read

env:
VIEWER_BUILD_OUTPUT_DIR: backend/FwLite/FwLiteShared/wwwroot/viewer
jobs:
Expand Down Expand Up @@ -146,12 +155,24 @@ jobs:
exit 1
fi
rm -f src/locales/en.po.old
- name: Set up Playwright dependencies
- name: Cache Playwright browsers
uses: actions/cache@v4
id: playwright-cache
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ hashFiles('frontend/pnpm-lock.yaml') }}
- name: Install Playwright browsers
if: steps.playwright-cache.outputs.cache-hit != 'true'
working-directory: frontend
run: pnpm exec playwright install --with-deps
- name: Run snapshot tests
- name: Install Playwright system deps
if: steps.playwright-cache.outputs.cache-hit == 'true'
working-directory: frontend
run: pnpm exec playwright install-deps
- name: Run UI tests
working-directory: frontend/viewer
run: task playwright-test-standalone
run: task test:ui-standalone

- name: Build viewer
working-directory: frontend/viewer
run: pnpm run build
Expand All @@ -170,6 +191,9 @@ jobs:

frontend-component-unit-tests:
runs-on: ubuntu-latest
permissions:
contents: read
checks: write
steps:
- name: Checkout
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
Expand All @@ -186,9 +210,20 @@ jobs:
run: |
pnpm install
# vitest is configured to use playwright for stories and other browser-based tests
- name: Set up Playwright dependencies
- name: Cache Playwright browsers
uses: actions/cache@v4
id: playwright-cache
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ hashFiles('frontend/pnpm-lock.yaml') }}
- name: Install Playwright browsers
if: steps.playwright-cache.outputs.cache-hit != 'true'
working-directory: frontend/viewer
run: pnpm exec playwright install --with-deps
- name: Install Playwright system deps
if: steps.playwright-cache.outputs.cache-hit == 'true'
working-directory: frontend/viewer
run: pnpm exec playwright install-deps
- name: vitest
working-directory: frontend/viewer
run: pnpm run test --retry=2 --reporter=default --reporter=junit --outputFile.junit=test-results/vitest-results.xml
Expand Down Expand Up @@ -459,3 +494,124 @@ jobs:
run: |
sleep 10
curl -X POST https://lexbox.org/api/fwlite-release/new-release


e2e-test:
name: E2E Tests
needs: [frontend]
runs-on: ubuntu-latest
timeout-minutes: 30
env:
FW_LITE_BINARY_PATH: ${{ github.workspace }}/backend/FwLite/artifacts/publish/FwLiteWeb/release_linux-x64/FwLiteWeb
steps:
- name: Checkout
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
with:
submodules: true
- uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
with:
name: fw-lite-viewer-js
path: ${{ env.VIEWER_BUILD_OUTPUT_DIR }}
- uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5.3.0
with:
dotnet-version: '10.x'
- name: Publish FwLiteWeb (linux-x64)
working-directory: backend/FwLite/FwLiteWeb
run: dotnet publish -r linux-x64 --artifacts-path ../artifacts -p:PublishSingleFile=true

- name: set execute permissions
shell: bash
run: chmod +x ${{ env.FW_LITE_BINARY_PATH }}

- name: Install Task
uses: arduino/setup-task@b91d5d2c96a56797b48ac1e0e89220bf64044611 #v2
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
with:
package_json_file: 'frontend/package.json'
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
with:
node-version-file: './frontend/package.json'
cache: 'pnpm'
cache-dependency-path: './frontend/pnpm-lock.yaml'
- name: Install frontend deps
working-directory: frontend
run: pnpm install
- name: Test FwLite launcher
working-directory: frontend/viewer
run: task test:launcher

- uses: ./.github/actions/setup-k8s
with:
lexbox-api-tag: develop
ingress-controller-port: '6579' # http; only used by the action's own curl-verify step
repo-token: ${{ secrets.GITHUB_TOKEN }}

# MSAL refuses any non-https authority. ingress-nginx serves a snake-oil
# cert that FwLite trusts via --environment Development and playwright via
# ignoreHTTPSErrors.
- name: Forward ingress HTTPS port
shell: bash
run: |
kubectl port-forward service/ingress-nginx-controller 6580:443 -n languagedepot >/tmp/https-port-forward.log 2>&1 &
# Wait for the port-forward to actually be listening
for i in $(seq 1 30); do
if curl -sk https://localhost:6580 >/dev/null 2>&1; then
echo "✅ HTTPS port-forward ready on :6580"
exit 0
fi
sleep 1
done
echo "❌ HTTPS port-forward did not become ready"
cat /tmp/https-port-forward.log
exit 1

- name: Cache Playwright browsers
uses: actions/cache@v4
id: playwright-cache
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ hashFiles('frontend/pnpm-lock.yaml') }}
- name: Install Playwright browsers
if: steps.playwright-cache.outputs.cache-hit != 'true'
working-directory: frontend/viewer
run: pnpm exec playwright install --with-deps
- name: Install Playwright system deps
if: steps.playwright-cache.outputs.cache-hit == 'true'
working-directory: frontend/viewer
run: pnpm exec playwright install-deps

- name: Run E2E tests
working-directory: frontend/viewer
env:
TEST_SERVER_PROTOCOL: https
TEST_SERVER_PORT: 6580
run: task test:e2e

- name: Upload Playwright results/traces (on failure)
if: failure()
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: fw-lite-e2e-test-results
if-no-files-found: ignore
path: frontend/viewer/tests/e2e/test-results/

- name: Capture k8s pod logs (on failure)
if: failure()
shell: bash
run: |
mkdir -p k8s-logs
for app in lexbox ui hg db fw-headless; do
kubectl describe pods -l "app=${app}" -n languagedepot > k8s-logs/describe-${app}.txt 2>&1 || true
kubectl logs -l "app=${app}" -n languagedepot --prefix --all-containers --tail=-1 > k8s-logs/logs-${app}.txt 2>&1 || true
done
kubectl logs -l 'app.kubernetes.io/name=ingress-nginx' -n languagedepot --prefix --all-containers --tail=-1 > k8s-logs/logs-ingress.txt 2>&1 || true

- name: Upload k8s logs (on failure)
if: failure()
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: fw-lite-e2e-k8s-logs
if-no-files-found: ignore
path: k8s-logs/*.txt
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
1 change: 1 addition & 0 deletions .github/workflows/integration-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ jobs:
timeout-minutes: 120
name: Dotnet tests on ${{ inputs.runs-on }} for Mercurial ${{ inputs.hg-version }} on ${{ inputs.environment }}
permissions:
contents: read
checks: write
environment:
name: ${{ inputs.environment }}
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/release-pipeline.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ on:
- main
workflow_dispatch:

permissions:
contents: read

jobs:
set-version:
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ Key documentation for this project:
- ✅ **`MiniLcm.Tests`, `LcmCrdt.Tests`, `FwDataMiniLcmBridge.Tests` need no infrastructure** — these are the unit/conformance tests for the two `IMiniLcmApi` implementations; run them filtered to your change like any unit test (this is where a `*TestsBase` change should be verified, including its FwData side). The FwData/LCM-backed selections load a real project (a handful of tests = seconds, a whole class = a minute or two), so keep the filter tight; don't reach for the whole class or project.
- ✅ **FwLite integration tests** (e.g. `FwLiteProjectSync.Tests`) need no infrastructure but are slow. FwData runs **in-process via liblcm** — these tests create real FieldWorks projects with no external setup (e.g. the `ProjectTemplateTests.GenerateTemplate` regen tool). Only FwHeadless's hg/Mercurial sync and the lexbox stack (last bullet) need running infrastructure — *not* FwData itself. Run a **targeted selection** (specific tests, not necessarily whole classes) when you touched critical sync code **and believe the work is finished** — not on every iteration. Waiting on tests burns time; be deliberate about which runs buy real signal.
- ✅ **`backend/Testing` contains unit tests too** — only tests marked `Category=Integration|FlakyIntegration|RequiresDb` (and the `Testing.Browser` namespace) need infrastructure. Its unit tests are fine to run: `task test:unit -- <filter>` excludes those categories for you.
- ✅ **FwLite viewer Playwright tests MAY be run** — they're cheap: `task playwright-test-standalone -- <test-name-filter>` (from `frontend/viewer/`) auto-starts the vite dev server with the in-browser demo project; no lexbox stack, chromium only. Always filter to relevant tests; details in `frontend/viewer/AGENTS.md`.
- ✅ **FwLite viewer Playwright tests MAY be run** — they're cheap: `task test:ui-standalone -- <test-name-filter>` (from `frontend/viewer/`) auto-starts the vite dev server with the in-browser demo project; no lexbox stack, chromium only. Always filter to relevant tests; details in `frontend/viewer/AGENTS.md`.
- ❌ **Do NOT run tests that need the lexbox stack** unless the user explicitly asks: LexBox integration tests (`Category=Integration`/`FlakyIntegration`, `Testing.Browser`) and the lexbox frontend Playwright suite (`frontend/tests`). The local stack is usually down or torn down between sessions and results aren't trustworthy — rely on CI for these.

### Questions?
Expand Down
2 changes: 2 additions & 0 deletions backend/FwLite/FwLiteWeb/FwLiteWebServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ public static WebApplication SetupAppServer(WebApplicationOptions options, Actio
options.AddFilter(new LockedProjectFilter());
options.EnableDetailedErrors = true;
}).AddJsonProtocol();
builder.Services.AddHealthChecks();

configure?.Invoke(builder);
var app = builder.Build();
Expand Down Expand Up @@ -124,6 +125,7 @@ public static WebApplication SetupAppServer(WebApplicationOptions options, Actio
app.MapImport();
app.MapAuthRoutes();
app.MapMiniLcmRoutes("/api/mini-lcm");
app.MapHealthChecks("/health");

app.MapStaticAssets();
app.MapRazorComponents<App>()
Expand Down
4 changes: 4 additions & 0 deletions backend/FwLite/FwLiteWeb/Routes/AuthRoutes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ public static IEndpointConventionBuilder MapAuthRoutes(this WebApplication app)
async (AuthService authService, string authority, IOptions<AuthConfig> options, [FromHeader] string referer) =>
{
var returnUrl = new Uri(referer).PathAndQuery;
if (returnUrl.StartsWith("/api/auth/login", StringComparison.Ordinal))
{
returnUrl = "/";
}
if (options.Value.SystemWebViewLogin)
{
throw new NotSupportedException("System web view login is not supported for this endpoint");
Expand Down
7 changes: 7 additions & 0 deletions backend/FwLite/FwLiteWeb/Routes/ProjectRoutes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,13 @@ [FromQuery] UserProjectRole? role
_ => Results.InternalServerError("DownloadProjectByCodeResult enum value not handled, please inform FW Lite devs")
};
});
group.MapDelete("/crdt/{code}",
async (CrdtProjectsService projectService, string code) =>
{
if (!projectService.ProjectExists(code)) return Results.NotFound($"Project {code} not found");
await projectService.DeleteProject(code);
return Results.Ok();
});
return group;
}

Expand Down
6 changes: 6 additions & 0 deletions deployment/gha/lexbox.patch.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ spec:
env:
- name: Email__BaseUrl
value: "http://localhost:6579"
# Trust the kind cluster's pod network so X-Forwarded-Proto from the
# ingress-nginx controller is honored — otherwise lexbox sees the request
# as HTTP and emits HTTP URLs in /.well-known/openid-configuration, which
# breaks the FwLite OAuth flow over the HTTPS port-forward used in CI.
- name: ForwardedHeadersOptions__KnownNetworks__0
value: "0.0.0.0/0"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
volumeMounts:
- mountPath: /frontend
name: gql-schema
Expand Down
1 change: 1 addition & 0 deletions frontend/viewer/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ storybook-static
screenshots/
__screenshots__/
eslint_report.json
**/*-html-report
31 changes: 19 additions & 12 deletions frontend/viewer/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,29 @@ task fw-lite:has-stale-generated-types

The generated files are located in `src/lib/dotnet-types/generated-types/`.

### E2E Testing (Playwright)
### Testing

| Suite | Location | Runnable here? |
|---|---|---|
| UI | `tests/ui/` | ✅ Yes — auto-starts a dev server with in-memory demo; no infra needed |
| E2E | `tests/e2e/` | ❌ Needs a Lexbox kind cluster + published FwLiteWeb binary |
| Launcher | `tests/launcher/` | ❌ Needs a published FwLiteWeb binary |

**Don't run E2E or Launcher tests unless you've explicitly set up that infrastructure** — they fail loudly without it and the setup isn't part of normal dev.

UI tests (the runnable ones) — from `frontend/viewer/`:

To run E2E tests for the viewer:
```bash
# From frontend/viewer/ directory
# Automatically starts dev server if needed
# For debugging e.g. with Chrome MCP: dev server/demo project will be available at port 5173 & path /testing/project-view/browse
# Dev server / demo project: http://localhost:5173/testing/project-view (useful for Chrome MCP debugging).

# Filter by test name (the ONLY RIGHT choice if testing specific features or changes) e.g.
task playwright-test-standalone -- entries-list
# Filter by test name (the ONLY RIGHT choice when testing specific features or changes), e.g.
task test:ui-standalone -- entries-list

# All tests
task playwright-test-standalone
# All UI tests
task test:ui-standalone

Comment thread
coderabbitai[bot] marked this conversation as resolved.
# In UI mode
task playwright-test-standalone -- entries-list --ui
# Playwright UI mode
task test:ui-standalone -- entries-list --ui
```

## Tech Stack
Expand All @@ -63,7 +70,7 @@ task playwright-test-standalone -- entries-list --ui
| `src/lib/` | Shared components, entry editor |
| `src/locales/` | i18n translation files (JSON) |
| `.storybook/` | Component storybook |
| `tests/` | Playwright E2E tests |
| `tests/` | Playwright (UI + e2e) and Vitest (launcher) tests |

## i18n (Lingui)

Expand Down
27 changes: 18 additions & 9 deletions frontend/viewer/Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,20 +42,29 @@ tasks:
test-unit:
cmd: pnpm test --project unit

playwright-test:
desc: 'runs playwright tests against already running server'
cmd: pnpm run test:playwright {{.CLI_ARGS}}
playwright-test-standalone:
test:ui:
desc: 'runs UI tests against already running server'
cmd: pnpm run test:ui {{.CLI_ARGS}}
test:ui-standalone:
aliases: [pts]
desc: 'runs playwright tests and runs dev automatically, run ui mode by calling with -- --ui or use --update-snapshots'
desc: 'runs UI tests and runs dev automatically; --ui for playwright UI mode, --update-snapshots to refresh visual baselines'
env:
AUTO_START_SERVER: true
cmd: pnpm run test:playwright {{.CLI_ARGS}}
cmd: pnpm run test:ui {{.CLI_ARGS}}
generate-marketing-screenshots:
desc: 'they should be in the screenshots folder'
env:
AUTO_START_SERVER: true
MARKETING_SCREENSHOTS: true
cmd: pnpm run test:playwright {{.CLI_ARGS}}
playwright-test-report:
cmd: pnpm run test:playwright-report
cmd: pnpm run test:ui snapshots {{.CLI_ARGS}}

test:e2e:
cmd: pnpm run test:e2e {{.CLI_ARGS}}
test:launcher:
desc: 'tests the fw lite launcher class against a real subprocess; FW_LITE_BINARY_PATH must point to a built binary'
cmd: pnpm test:launcher --run
test:build-launcher:
desc: 'publishes a self-contained FwLiteWeb binary into ./dist/fw-lite-server; prerequisite for test:e2e and test:launcher'
deps: [build]
cmds:
- dotnet publish ../../backend/FwLite/FwLiteWeb/FwLiteWeb.csproj --configuration Release --self-contained --output ./dist/fw-lite-server
Comment thread
myieye marked this conversation as resolved.
13 changes: 7 additions & 6 deletions frontend/viewer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,17 @@
"build": "vite build",
"build-ffmpeg-worker": "vite build --config vite.config.ffmpeg-worker.ts",
"preview": "vite preview",
"pretest:playwright": "playwright install",
"test:playwright": "playwright test",
"test:playwright-report": "playwright show-report html-test-results",
"test:playwright-record": "playwright codegen",
"test": "vitest run",
"test:ui": "vitest --ui",
"pretest:ui": "playwright install chromium",
"test:ui": "playwright test -c ./tests/ui/playwright.config.ts",
"pretest:e2e": "playwright install chromium",
"test:e2e": "playwright test -c ./tests/e2e/playwright.config.ts",
"test": "vitest run --project=!launcher",
"test:watch": "vitest",
"test:storybook": "vitest --project=storybook",
"test:unit": "vitest --project=unit",
"test:browser": "vitest --project=browser",
"test:launcher": "vitest --project=launcher",
"test:vitest-ui": "vitest --ui",
"check": "svelte-check",
"format": "prettier --write .",
"format:check": "prettier --check .",
Expand Down
Loading
Loading