-
-
Notifications
You must be signed in to change notification settings - Fork 9.2k
🎭 feat: Add Credential-Free Playwright Smoke Suite with a Local Mock LLM #13472
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
d25f460
🧪 feat: add e2e playwright tests
teresama 8eeb0af
🧪 feat: Add Playwright Recording Harness
danny-avila bc06104
test: fix mock playwright config
danny-avila dc4a146
test: harden mock e2e environment
danny-avila 0638f41
test: preserve mock dotenv secrets
danny-avila b4cad4b
test: harden mock isolation setup
danny-avila 5b1aaf9
ci: cache mock e2e builds
danny-avila e156b66
test: harden e2e cache and recorder checks
danny-avila 805b5bd
Merge remote-tracking branch 'origin/dev' into teresama/add-playwrigh…
danny-avila b5d9add
test: preserve data-provider exports in oauth route test
danny-avila 03ba9a9
test: isolate mock auth logout state
danny-avila 53be837
test: allow isolated logout smoke setup
danny-avila 410237c
test: prepare logout smoke auth via api
danny-avila 5877ab5
test: isolate oauth route module mock
danny-avila File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,143 @@ | ||
| name: Playwright E2E (Mock LLM) | ||
|
|
||
| on: | ||
| pull_request: | ||
| workflow_dispatch: | ||
| inputs: | ||
| reason: | ||
| description: 'Reason for manual trigger' | ||
| required: false | ||
| default: 'Manual e2e run' | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| concurrency: | ||
| group: playwright-mock-${{ github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| env: | ||
| NODE_OPTIONS: '--max-old-space-size=${{ secrets.NODE_MAX_OLD_SPACE_SIZE || 6144 }}' | ||
|
|
||
| jobs: | ||
| e2e: | ||
| name: Tier-1 smoke (headless Chromium) | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 30 | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - name: Use Node.js 24.16.0 | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: '24.16.0' | ||
|
|
||
| - name: Restore node_modules cache | ||
| id: cache-node-modules | ||
| uses: actions/cache@v4 | ||
| with: | ||
| path: | | ||
| node_modules | ||
| client/node_modules | ||
| packages/client/node_modules | ||
| packages/data-provider/node_modules | ||
| packages/data-schemas/node_modules | ||
| packages/api/node_modules | ||
| api/node_modules | ||
| key: node-modules-e2e-${{ runner.os }}-24.16.0-${{ hashFiles('package-lock.json') }} | ||
|
|
||
| - name: Install dependencies | ||
| if: steps.cache-node-modules.outputs.cache-hit != 'true' | ||
| run: npm ci | ||
|
|
||
| - name: Restore data-provider build cache | ||
| id: cache-data-provider | ||
| uses: actions/cache@v4 | ||
| with: | ||
| path: packages/data-provider/dist | ||
| key: build-data-provider-${{ runner.os }}-${{ hashFiles('package-lock.json', 'packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/rollup.config.js', 'packages/data-provider/package.json') }} | ||
|
|
||
| - name: Build data-provider | ||
| if: steps.cache-data-provider.outputs.cache-hit != 'true' | ||
| run: npm run build:data-provider | ||
|
|
||
| - name: Restore data-schemas build cache | ||
| id: cache-data-schemas | ||
| uses: actions/cache@v4 | ||
| with: | ||
| path: packages/data-schemas/dist | ||
| key: build-data-schemas-${{ runner.os }}-${{ hashFiles('package-lock.json', 'packages/data-schemas/src/**', 'packages/data-schemas/tsconfig*.json', 'packages/data-schemas/rollup.config.js', 'packages/data-schemas/package.json', 'packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/rollup.config.js', 'packages/data-provider/package.json') }} | ||
|
|
||
| - name: Build data-schemas | ||
| if: steps.cache-data-schemas.outputs.cache-hit != 'true' | ||
| run: npm run build:data-schemas | ||
|
|
||
| - name: Restore api build cache | ||
| id: cache-api | ||
| uses: actions/cache@v4 | ||
| with: | ||
| path: packages/api/dist | ||
| key: build-api-${{ runner.os }}-${{ hashFiles('package-lock.json', 'packages/api/src/**', 'packages/api/tsconfig*.json', 'packages/api/server-rollup.config.js', 'packages/api/package.json', 'packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/rollup.config.js', 'packages/data-provider/package.json', 'packages/data-schemas/src/**', 'packages/data-schemas/tsconfig*.json', 'packages/data-schemas/rollup.config.js', 'packages/data-schemas/package.json') }} | ||
|
|
||
| - name: Build api | ||
| if: steps.cache-api.outputs.cache-hit != 'true' | ||
| run: npm run build:api | ||
|
|
||
| - name: Restore client-package build cache | ||
| id: cache-client-package | ||
| uses: actions/cache@v4 | ||
| with: | ||
| path: packages/client/dist | ||
| key: build-client-package-${{ runner.os }}-${{ hashFiles('package-lock.json', 'packages/client/src/**', 'packages/client/tsconfig*.json', 'packages/client/rollup.config.js', 'packages/client/package.json', 'packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/rollup.config.js', 'packages/data-provider/package.json') }} | ||
|
|
||
| - name: Build client-package | ||
| if: steps.cache-client-package.outputs.cache-hit != 'true' | ||
| run: npm run build:client-package | ||
|
|
||
| - name: Restore client app build cache | ||
| id: cache-client-app | ||
| uses: actions/cache@v4 | ||
| with: | ||
| path: client/dist | ||
| key: build-client-app-e2e-${{ runner.os }}-${{ hashFiles('package-lock.json', 'client/src/**', 'client/public/**', 'client/scripts/post-build.cjs', 'client/index.html', 'client/package.json', 'client/vite.config.*', 'client/tsconfig*.json', 'client/tailwind.config.*', 'client/postcss.config.*', 'packages/client/src/**', 'packages/client/tsconfig*.json', 'packages/client/rollup.config.js', 'packages/client/package.json', 'packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/rollup.config.js', 'packages/data-provider/package.json') }} | ||
|
|
||
| - name: Build client app | ||
| if: steps.cache-client-app.outputs.cache-hit != 'true' | ||
| run: npm run build:client | ||
|
|
||
| - name: Resolve Playwright version | ||
| id: pw | ||
| run: echo "version=$(node -p "require('@playwright/test/package.json').version")" >> "$GITHUB_OUTPUT" | ||
|
|
||
| - name: Cache Playwright browsers | ||
| id: cache-playwright | ||
| uses: actions/cache@v4 | ||
| with: | ||
| path: ~/.cache/ms-playwright | ||
| key: playwright-${{ runner.os }}-${{ steps.pw.outputs.version }} | ||
|
|
||
| - name: Install Playwright Chromium | ||
| run: npx playwright install --with-deps chromium | ||
|
|
||
| - name: Run mock-LLM Tier-1 e2e | ||
| run: npx playwright test --config=e2e/playwright.config.mock.ts | ||
| env: | ||
| CI: 'true' | ||
|
|
||
| - name: Upload Playwright HTML report | ||
| if: ${{ !cancelled() }} | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: playwright-report | ||
| path: e2e/playwright-report/** | ||
| retention-days: 7 | ||
| if-no-files-found: ignore | ||
|
|
||
| - name: Upload traces & screenshots | ||
| if: failure() | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: playwright-test-results | ||
| path: e2e/specs/.test-results/** | ||
| retention-days: 7 | ||
| if-no-files-found: ignore | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| # LibreChat e2e | ||
|
|
||
| The mock e2e profile is the safest default for generated tests. It starts LibreChat with `e2e/config/librechat.e2e.yaml`, points custom endpoints at the local mock LLM server, creates an authenticated e2e user, and avoids real provider credentials. | ||
|
|
||
| ## Recording Tests | ||
|
|
||
| Use Playwright codegen when you want to turn an exploratory browser session into a draft test: | ||
|
|
||
| ```sh | ||
| npm run e2e:record | ||
| ``` | ||
|
|
||
| That command builds the app, starts the mock LLM and LibreChat test server when needed, writes `e2e/storageState.json`, and opens Playwright codegen at `/c/new`. The npm script uses `http://localhost:3333` so it does not collide with a normal dev server on `3080`. Raw recordings are written to `e2e/recordings/` and ignored by git. | ||
|
|
||
| For a real local LibreChat config instead of the mock LLM profile: | ||
|
|
||
| ```sh | ||
| npm run e2e:record:local | ||
| ``` | ||
|
|
||
| Useful direct options: | ||
|
|
||
| ```sh | ||
| node e2e/setup/record.js --url=http://localhost:3080/c/new | ||
| node e2e/setup/record.js --profile=local --no-output | ||
| node e2e/setup/record.js --auth-only | ||
| node e2e/setup/record.js --output=e2e/recordings/settings-draft.spec.ts | ||
| ``` | ||
|
|
||
| ## LLM-Assisted Loop | ||
|
|
||
| 1. Start `npm run e2e:record`. | ||
| 2. Let the LLM use Computer Use to operate the headed Playwright browser. | ||
| 3. Stop codegen after the workflow is captured. | ||
| 4. Move the useful parts from `e2e/recordings/` into a committed spec under `e2e/specs/mock/`. | ||
| 5. Replace brittle generated selectors with role, label, text, or `data-testid` locators. | ||
| 6. Add assertions that prove the behavior, not just the clicked path. | ||
| 7. Run the finished spec with `npm run e2e:mock -- <spec name>`. | ||
|
|
||
| Generated recordings are a draft, not the final test. The committed version should use the shared helpers in `e2e/specs/mock/helpers.ts` where possible, wait on network or visible UI state instead of fixed sleeps, and keep test data deterministic. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| # Credential-free e2e config template. e2e/playwright.config.mock.ts writes | ||
| # an ignored runtime copy and rewrites the mock LLM port from MOCK_LLM_PORT. | ||
| version: 1.3.11 | ||
| cache: true | ||
|
|
||
| endpoints: | ||
| custom: | ||
| - name: 'Mock Provider A' | ||
| apiKey: 'e2e-mock-key-a' | ||
| baseURL: 'http://127.0.0.1:8889/v1' | ||
|
danny-avila marked this conversation as resolved.
|
||
| models: | ||
| default: | ||
| - 'mock-model-a' | ||
| fetch: false | ||
| titleConvo: false | ||
| modelDisplayLabel: 'Mock Provider A' | ||
|
|
||
| - name: 'Mock Provider B' | ||
| apiKey: 'e2e-mock-key-b' | ||
| baseURL: 'http://127.0.0.1:8889/v1' | ||
| models: | ||
| default: | ||
| - 'mock-model-b' | ||
| fetch: false | ||
| titleConvo: false | ||
| modelDisplayLabel: 'Mock Provider B' | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.