test: Use Playwright + agenda start/end dates fix (#4471)
* test: agenda-neue - separate timezone controls assertions to allow retries * test: agenda-neue - use dom query selectors instead of first() / eq() * test: agenda-neue - playwright * test: fix playwright setup for ci * test: playwright - remove safari + fix timezone local * test: upload playwright report * test: playwright - fix trace upload * test * test: playwright - agenda search * test: fix startdate timezone * test: playwright - agenda table events * test: playwright - remove only filter * test: remove exit early flag * test: allow longer tests * test: agenda materials dialog * test: agenda filter by area/group * test: agenda calendar view * test: agenda settings * test: jump to day * test: fix agenda jump to day timezone parse * test: increase test timeout * test: remove fail fast * test: test sharding + increase delay * test: fixes * test: use macos image * test: fixes * test: agenda color assign + future + live meeting tests * test: agenda mobile tests * test: remainder of tests for playwright + optimizations * test: red line intersection accept close value * test: add delay for agenda search tests * chore: cleanup old tests + adapt build workflow * ci: fix build workflow * ci: fix build workflow order * fix: point to playwright floor plan images + readme
44
.github/workflows/build.yml
vendored
|
@ -171,42 +171,46 @@ jobs:
|
||||||
name: coverage
|
name: coverage
|
||||||
path: coverage.json
|
path: coverage.json
|
||||||
|
|
||||||
tests-cypress-modern:
|
tests-playwright:
|
||||||
name: Run Tests (Cypress - Modern)
|
name: Run Tests (Playwright)
|
||||||
if: ${{ github.event.inputs.skiptests == 'false' }}
|
if: ${{ github.event.inputs.skiptests == 'false' }}
|
||||||
needs: [prepare]
|
needs: [prepare]
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
project: [chromium, firefox]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: '18'
|
||||||
|
|
||||||
- name: Run all tests
|
- name: Run all tests
|
||||||
run: |
|
run: |
|
||||||
echo "Installing dependencies..."
|
echo "Installing dependencies..."
|
||||||
yarn
|
yarn
|
||||||
echo "Start Vite Preview..."
|
echo "Installing Playwright..."
|
||||||
yarn preview &>/dev/null &
|
cd playwright
|
||||||
|
mkdir test-results
|
||||||
|
npm ci
|
||||||
|
npx playwright install --with-deps ${{ matrix.project }}
|
||||||
echo "Running tests..."
|
echo "Running tests..."
|
||||||
yarn cypress
|
npx playwright test --project=${{ matrix.project }}
|
||||||
|
|
||||||
- name: Upload Video Recordings
|
- name: Upload Report
|
||||||
uses: actions/upload-artifact@v3.0.0
|
uses: actions/upload-artifact@v3.0.0
|
||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
name: videos-modern
|
name: playwright-results-${{ matrix.project }}
|
||||||
path: cypress/videos/
|
path: playwright/test-results/
|
||||||
|
if-no-files-found: ignore
|
||||||
- name: Upload Screenshots
|
|
||||||
uses: actions/upload-artifact@v3.0.0
|
|
||||||
if: ${{ always() }}
|
|
||||||
continue-on-error: true
|
|
||||||
with:
|
|
||||||
name: screenshots-modern
|
|
||||||
path: cypress/screenshots/
|
|
||||||
|
|
||||||
tests-cypress-legacy:
|
tests-cypress:
|
||||||
name: Run Tests (Cypress - Legacy)
|
name: Run Tests (Cypress)
|
||||||
if: ${{ github.event.inputs.skiptests == 'false' }}
|
if: ${{ github.event.inputs.skiptests == 'false' }}
|
||||||
needs: [prepare]
|
needs: [prepare]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
@ -256,6 +260,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
name: videos-legacy
|
name: videos-legacy
|
||||||
path: cypress/videos/
|
path: cypress/videos/
|
||||||
|
if-no-files-found: ignore
|
||||||
|
|
||||||
- name: Upload Screenshots
|
- name: Upload Screenshots
|
||||||
uses: actions/upload-artifact@v3.0.0
|
uses: actions/upload-artifact@v3.0.0
|
||||||
|
@ -264,6 +269,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
name: screenshots-modern
|
name: screenshots-modern
|
||||||
path: cypress/screenshots/
|
path: cypress/screenshots/
|
||||||
|
if-no-files-found: ignore
|
||||||
|
|
||||||
# -----------------------------------------------------------------
|
# -----------------------------------------------------------------
|
||||||
# RELEASE
|
# RELEASE
|
||||||
|
@ -271,7 +277,7 @@ jobs:
|
||||||
release:
|
release:
|
||||||
name: Make Release
|
name: Make Release
|
||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
needs: [tests-python, tests-cypress-modern, tests-cypress-legacy, prepare]
|
needs: [tests-python, tests-playwright, tests-cypress, prepare]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
SHOULD_DEPLOY: ${{needs.prepare.outputs.should_deploy}}
|
SHOULD_DEPLOY: ${{needs.prepare.outputs.should_deploy}}
|
||||||
|
|
63
.pnp.cjs
generated
|
@ -61,6 +61,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||||
["eslint-config-standard", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:17.0.0"],\
|
["eslint-config-standard", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:17.0.0"],\
|
||||||
["eslint-plugin-cypress", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:2.12.1"],\
|
["eslint-plugin-cypress", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:2.12.1"],\
|
||||||
["eslint-plugin-import", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:2.26.0"],\
|
["eslint-plugin-import", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:2.26.0"],\
|
||||||
|
["eslint-plugin-n", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:15.2.5"],\
|
||||||
["eslint-plugin-node", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:11.1.0"],\
|
["eslint-plugin-node", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:11.1.0"],\
|
||||||
["eslint-plugin-promise", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.0.1"],\
|
["eslint-plugin-promise", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.0.1"],\
|
||||||
["eslint-plugin-vue", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:9.4.0"],\
|
["eslint-plugin-vue", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:9.4.0"],\
|
||||||
|
@ -3095,10 +3096,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||||
}]\
|
}]\
|
||||||
]],\
|
]],\
|
||||||
["builtins", [\
|
["builtins", [\
|
||||||
["npm:4.1.0", {\
|
["npm:5.0.1", {\
|
||||||
"packageLocation": "./.yarn/cache/builtins-npm-4.1.0-b8969ccdfe-3524f5a589.zip/node_modules/builtins/",\
|
"packageLocation": "./.yarn/cache/builtins-npm-5.0.1-6d4820dd76-66d204657f.zip/node_modules/builtins/",\
|
||||||
"packageDependencies": [\
|
"packageDependencies": [\
|
||||||
["builtins", "npm:4.1.0"],\
|
["builtins", "npm:5.0.1"],\
|
||||||
["semver", "npm:7.3.7"]\
|
["semver", "npm:7.3.7"]\
|
||||||
],\
|
],\
|
||||||
"linkType": "HARD"\
|
"linkType": "HARD"\
|
||||||
|
@ -4862,7 +4863,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||||
["@types/eslint-plugin-promise", null],\
|
["@types/eslint-plugin-promise", null],\
|
||||||
["eslint", "npm:8.23.0"],\
|
["eslint", "npm:8.23.0"],\
|
||||||
["eslint-plugin-import", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:2.26.0"],\
|
["eslint-plugin-import", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:2.26.0"],\
|
||||||
["eslint-plugin-n", "virtual:5de208ba69f1abc06b9e76e02d4ce4fc49fcab4f9a21e07e70644bde43652129abc0a8d2e76e362701d48e3019da8bcf1b9d697031ba71659718edb10a775408#npm:15.2.0"],\
|
["eslint-plugin-n", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:15.2.5"],\
|
||||||
["eslint-plugin-promise", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.0.1"]\
|
["eslint-plugin-promise", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.0.1"]\
|
||||||
],\
|
],\
|
||||||
"packagePeers": [\
|
"packagePeers": [\
|
||||||
|
@ -4871,6 +4872,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||||
"@types/eslint-plugin-promise",\
|
"@types/eslint-plugin-promise",\
|
||||||
"@types/eslint",\
|
"@types/eslint",\
|
||||||
"eslint-plugin-import",\
|
"eslint-plugin-import",\
|
||||||
|
"eslint-plugin-n",\
|
||||||
"eslint-plugin-promise",\
|
"eslint-plugin-promise",\
|
||||||
"eslint"\
|
"eslint"\
|
||||||
],\
|
],\
|
||||||
|
@ -4962,10 +4964,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||||
],\
|
],\
|
||||||
"linkType": "SOFT"\
|
"linkType": "SOFT"\
|
||||||
}],\
|
}],\
|
||||||
["virtual:5cccaf00e87dfff96dbbb5eaf7a3055373358b8114d6a1adfb32f54ed6b40ba06068d3aa1fdd8062899a0cad040f68c17cc6b72bac2cdbe9700f3d6330d112f3#npm:3.0.1", {\
|
["virtual:01ca464921c9b9f6f05ff324a93e38459f6f1b69ef551b0ff88f975ab15594aa2d383b8c5900d3e9ebd3f172ab1a572bfe06ea18bc9ed6aab7ddec552080fdee#npm:4.1.0", {\
|
||||||
"packageLocation": "./.yarn/__virtual__/eslint-plugin-es-virtual-9a126af2f5/0/cache/eslint-plugin-es-npm-3.0.1-95e8015220-e57592c523.zip/node_modules/eslint-plugin-es/",\
|
"packageLocation": "./.yarn/__virtual__/eslint-plugin-es-virtual-ecad70e987/0/cache/eslint-plugin-es-npm-4.1.0-a4cf26d3cd-26b87a216d.zip/node_modules/eslint-plugin-es/",\
|
||||||
"packageDependencies": [\
|
"packageDependencies": [\
|
||||||
["eslint-plugin-es", "virtual:5cccaf00e87dfff96dbbb5eaf7a3055373358b8114d6a1adfb32f54ed6b40ba06068d3aa1fdd8062899a0cad040f68c17cc6b72bac2cdbe9700f3d6330d112f3#npm:3.0.1"],\
|
["eslint-plugin-es", "virtual:01ca464921c9b9f6f05ff324a93e38459f6f1b69ef551b0ff88f975ab15594aa2d383b8c5900d3e9ebd3f172ab1a572bfe06ea18bc9ed6aab7ddec552080fdee#npm:4.1.0"],\
|
||||||
["@types/eslint", null],\
|
["@types/eslint", null],\
|
||||||
["eslint", "npm:8.23.0"],\
|
["eslint", "npm:8.23.0"],\
|
||||||
["eslint-utils", "npm:2.1.0"],\
|
["eslint-utils", "npm:2.1.0"],\
|
||||||
|
@ -4977,10 +4979,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||||
],\
|
],\
|
||||||
"linkType": "HARD"\
|
"linkType": "HARD"\
|
||||||
}],\
|
}],\
|
||||||
["virtual:c8e07c2a6b56869a5a6a64a5a2bbfafd2756fdfec2e39799aafeb55edf967e3394125f03e2dbe23e573456f9dc23ed0ee125b7a467ac99d6b6dc124c1fb861dd#npm:4.1.0", {\
|
["virtual:5cccaf00e87dfff96dbbb5eaf7a3055373358b8114d6a1adfb32f54ed6b40ba06068d3aa1fdd8062899a0cad040f68c17cc6b72bac2cdbe9700f3d6330d112f3#npm:3.0.1", {\
|
||||||
"packageLocation": "./.yarn/__virtual__/eslint-plugin-es-virtual-2fffa83c77/0/cache/eslint-plugin-es-npm-4.1.0-a4cf26d3cd-26b87a216d.zip/node_modules/eslint-plugin-es/",\
|
"packageLocation": "./.yarn/__virtual__/eslint-plugin-es-virtual-9a126af2f5/0/cache/eslint-plugin-es-npm-3.0.1-95e8015220-e57592c523.zip/node_modules/eslint-plugin-es/",\
|
||||||
"packageDependencies": [\
|
"packageDependencies": [\
|
||||||
["eslint-plugin-es", "virtual:c8e07c2a6b56869a5a6a64a5a2bbfafd2756fdfec2e39799aafeb55edf967e3394125f03e2dbe23e573456f9dc23ed0ee125b7a467ac99d6b6dc124c1fb861dd#npm:4.1.0"],\
|
["eslint-plugin-es", "virtual:5cccaf00e87dfff96dbbb5eaf7a3055373358b8114d6a1adfb32f54ed6b40ba06068d3aa1fdd8062899a0cad040f68c17cc6b72bac2cdbe9700f3d6330d112f3#npm:3.0.1"],\
|
||||||
["@types/eslint", null],\
|
["@types/eslint", null],\
|
||||||
["eslint", "npm:8.23.0"],\
|
["eslint", "npm:8.23.0"],\
|
||||||
["eslint-utils", "npm:2.1.0"],\
|
["eslint-utils", "npm:2.1.0"],\
|
||||||
|
@ -5033,27 +5035,27 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||||
}]\
|
}]\
|
||||||
]],\
|
]],\
|
||||||
["eslint-plugin-n", [\
|
["eslint-plugin-n", [\
|
||||||
["npm:15.2.0", {\
|
["npm:15.2.5", {\
|
||||||
"packageLocation": "./.yarn/cache/eslint-plugin-n-npm-15.2.0-669e8b723e-4303dea35a.zip/node_modules/eslint-plugin-n/",\
|
"packageLocation": "./.yarn/cache/eslint-plugin-n-npm-15.2.5-1f66a5b3be-3be265957b.zip/node_modules/eslint-plugin-n/",\
|
||||||
"packageDependencies": [\
|
"packageDependencies": [\
|
||||||
["eslint-plugin-n", "npm:15.2.0"]\
|
["eslint-plugin-n", "npm:15.2.5"]\
|
||||||
],\
|
],\
|
||||||
"linkType": "SOFT"\
|
"linkType": "SOFT"\
|
||||||
}],\
|
}],\
|
||||||
["virtual:5de208ba69f1abc06b9e76e02d4ce4fc49fcab4f9a21e07e70644bde43652129abc0a8d2e76e362701d48e3019da8bcf1b9d697031ba71659718edb10a775408#npm:15.2.0", {\
|
["virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:15.2.5", {\
|
||||||
"packageLocation": "./.yarn/__virtual__/eslint-plugin-n-virtual-c8e07c2a6b/0/cache/eslint-plugin-n-npm-15.2.0-669e8b723e-4303dea35a.zip/node_modules/eslint-plugin-n/",\
|
"packageLocation": "./.yarn/__virtual__/eslint-plugin-n-virtual-01ca464921/0/cache/eslint-plugin-n-npm-15.2.5-1f66a5b3be-3be265957b.zip/node_modules/eslint-plugin-n/",\
|
||||||
"packageDependencies": [\
|
"packageDependencies": [\
|
||||||
["eslint-plugin-n", "virtual:5de208ba69f1abc06b9e76e02d4ce4fc49fcab4f9a21e07e70644bde43652129abc0a8d2e76e362701d48e3019da8bcf1b9d697031ba71659718edb10a775408#npm:15.2.0"],\
|
["eslint-plugin-n", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:15.2.5"],\
|
||||||
["@types/eslint", null],\
|
["@types/eslint", null],\
|
||||||
["builtins", "npm:4.1.0"],\
|
["builtins", "npm:5.0.1"],\
|
||||||
["eslint", "npm:8.23.0"],\
|
["eslint", "npm:8.23.0"],\
|
||||||
["eslint-plugin-es", "virtual:c8e07c2a6b56869a5a6a64a5a2bbfafd2756fdfec2e39799aafeb55edf967e3394125f03e2dbe23e573456f9dc23ed0ee125b7a467ac99d6b6dc124c1fb861dd#npm:4.1.0"],\
|
["eslint-plugin-es", "virtual:01ca464921c9b9f6f05ff324a93e38459f6f1b69ef551b0ff88f975ab15594aa2d383b8c5900d3e9ebd3f172ab1a572bfe06ea18bc9ed6aab7ddec552080fdee#npm:4.1.0"],\
|
||||||
["eslint-utils", "virtual:4ce1a8504abbef475a07bd77b2f33ca024b044ac2c2313238806e468cfc919e5b56d220f7037cae852750a0611def1618a4dcf884e6a3971f5446ffa4429df13#npm:3.0.0"],\
|
["eslint-utils", "virtual:4ce1a8504abbef475a07bd77b2f33ca024b044ac2c2313238806e468cfc919e5b56d220f7037cae852750a0611def1618a4dcf884e6a3971f5446ffa4429df13#npm:3.0.0"],\
|
||||||
["ignore", "npm:5.2.0"],\
|
["ignore", "npm:5.2.0"],\
|
||||||
["is-core-module", "npm:2.9.0"],\
|
["is-core-module", "npm:2.10.0"],\
|
||||||
["minimatch", "npm:3.1.2"],\
|
["minimatch", "npm:3.1.2"],\
|
||||||
["resolve", "patch:resolve@npm%3A1.22.0#~builtin<compat/resolve>::version=1.22.0&hash=07638b"],\
|
["resolve", "patch:resolve@npm%3A1.22.1#~builtin<compat/resolve>::version=1.22.1&hash=07638b"],\
|
||||||
["semver", "npm:6.3.0"]\
|
["semver", "npm:7.3.7"]\
|
||||||
],\
|
],\
|
||||||
"packagePeers": [\
|
"packagePeers": [\
|
||||||
"@types/eslint",\
|
"@types/eslint",\
|
||||||
|
@ -6291,6 +6293,14 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||||
}]\
|
}]\
|
||||||
]],\
|
]],\
|
||||||
["is-core-module", [\
|
["is-core-module", [\
|
||||||
|
["npm:2.10.0", {\
|
||||||
|
"packageLocation": "./.yarn/cache/is-core-module-npm-2.10.0-6dff9310aa-0f3f77811f.zip/node_modules/is-core-module/",\
|
||||||
|
"packageDependencies": [\
|
||||||
|
["is-core-module", "npm:2.10.0"],\
|
||||||
|
["has", "npm:1.0.3"]\
|
||||||
|
],\
|
||||||
|
"linkType": "HARD"\
|
||||||
|
}],\
|
||||||
["npm:2.9.0", {\
|
["npm:2.9.0", {\
|
||||||
"packageLocation": "./.yarn/cache/is-core-module-npm-2.9.0-5ba77c35ae-b27034318b.zip/node_modules/is-core-module/",\
|
"packageLocation": "./.yarn/cache/is-core-module-npm-2.9.0-5ba77c35ae-b27034318b.zip/node_modules/is-core-module/",\
|
||||||
"packageDependencies": [\
|
"packageDependencies": [\
|
||||||
|
@ -8417,6 +8427,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||||
["supports-preserve-symlinks-flag", "npm:1.0.0"]\
|
["supports-preserve-symlinks-flag", "npm:1.0.0"]\
|
||||||
],\
|
],\
|
||||||
"linkType": "HARD"\
|
"linkType": "HARD"\
|
||||||
|
}],\
|
||||||
|
["patch:resolve@npm%3A1.22.1#~builtin<compat/resolve>::version=1.22.1&hash=07638b", {\
|
||||||
|
"packageLocation": "./.yarn/cache/resolve-patch-46f9469d0d-5656f4d0be.zip/node_modules/resolve/",\
|
||||||
|
"packageDependencies": [\
|
||||||
|
["resolve", "patch:resolve@npm%3A1.22.1#~builtin<compat/resolve>::version=1.22.1&hash=07638b"],\
|
||||||
|
["is-core-module", "npm:2.10.0"],\
|
||||||
|
["path-parse", "npm:1.0.7"],\
|
||||||
|
["supports-preserve-symlinks-flag", "npm:1.0.0"]\
|
||||||
|
],\
|
||||||
|
"linkType": "HARD"\
|
||||||
}]\
|
}]\
|
||||||
]],\
|
]],\
|
||||||
["resolve-from", [\
|
["resolve-from", [\
|
||||||
|
@ -8528,6 +8548,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||||
["eslint-config-standard", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:17.0.0"],\
|
["eslint-config-standard", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:17.0.0"],\
|
||||||
["eslint-plugin-cypress", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:2.12.1"],\
|
["eslint-plugin-cypress", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:2.12.1"],\
|
||||||
["eslint-plugin-import", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:2.26.0"],\
|
["eslint-plugin-import", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:2.26.0"],\
|
||||||
|
["eslint-plugin-n", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:15.2.5"],\
|
||||||
["eslint-plugin-node", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:11.1.0"],\
|
["eslint-plugin-node", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:11.1.0"],\
|
||||||
["eslint-plugin-promise", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.0.1"],\
|
["eslint-plugin-promise", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.0.1"],\
|
||||||
["eslint-plugin-vue", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:9.4.0"],\
|
["eslint-plugin-vue", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:9.4.0"],\
|
||||||
|
|
BIN
.yarn/cache/builtins-npm-5.0.1-6d4820dd76-66d204657f.zip
vendored
Normal file
BIN
.yarn/cache/eslint-plugin-n-npm-15.2.5-1f66a5b3be-3be265957b.zip
vendored
Normal file
BIN
.yarn/cache/is-core-module-npm-2.10.0-6dff9310aa-0f3f77811f.zip
vendored
Normal file
BIN
.yarn/cache/resolve-npm-1.22.1-3980488690-07af5fc1e8.zip
vendored
Normal file
BIN
.yarn/cache/resolve-patch-46f9469d0d-5656f4d0be.zip
vendored
Normal file
|
@ -24,10 +24,6 @@ const agendaStore = useAgendaStore()
|
||||||
|
|
||||||
const appContainer = ref(null)
|
const appContainer = ref(null)
|
||||||
|
|
||||||
// INIT
|
|
||||||
|
|
||||||
agendaStore.fetch()
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------
|
// --------------------------------------------------------------------
|
||||||
// Handle browser resize
|
// Handle browser resize
|
||||||
// --------------------------------------------------------------------
|
// --------------------------------------------------------------------
|
||||||
|
|
|
@ -227,8 +227,8 @@ const titleExtra = computed(() => {
|
||||||
return title
|
return title
|
||||||
})
|
})
|
||||||
const meetingDate = computed(() => {
|
const meetingDate = computed(() => {
|
||||||
const start = DateTime.fromISO(agendaStore.meeting.startDate).setZone(agendaStore.timezone)
|
const start = DateTime.fromISO(agendaStore.meeting.startDate, { zone: agendaStore.meeting.timezone }).setZone(agendaStore.timezone)
|
||||||
const end = DateTime.fromISO(agendaStore.meeting.endDate).setZone(agendaStore.timezone)
|
const end = DateTime.fromISO(agendaStore.meeting.endDate, { zone: agendaStore.meeting.timezone }).setZone(agendaStore.timezone)
|
||||||
if (start.month === end.month) {
|
if (start.month === end.month) {
|
||||||
return `${start.toFormat('MMMM d')} - ${end.toFormat('d, y')}`
|
return `${start.toFormat('MMMM d')} - ${end.toFormat('d, y')}`
|
||||||
} else {
|
} else {
|
||||||
|
@ -353,6 +353,8 @@ onBeforeUnmount(() => {
|
||||||
// MOUNTED
|
// MOUNTED
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
agendaStore.fetch(route.params.meetingNumber)
|
||||||
|
|
||||||
handleCurrentMeetingRedirect()
|
handleCurrentMeetingRedirect()
|
||||||
|
|
||||||
// -> Hide Loading Screen
|
// -> Hide Loading Screen
|
||||||
|
|
|
@ -31,7 +31,7 @@ n-drawer(v-model:show='isShown', placement='right', :width='panelWidth')
|
||||||
n-divider(title-placement='left')
|
n-divider(title-placement='left')
|
||||||
i.bi.bi-globe.me-2
|
i.bi.bi-globe.me-2
|
||||||
small Timezone
|
small Timezone
|
||||||
n-button-group.mt-2(style='justify-content: stretch; width: 100%;')
|
n-button-group.mt-2#agenda-settings-tz-btn(style='justify-content: stretch; width: 100%;')
|
||||||
n-button(
|
n-button(
|
||||||
style='flex-grow: 1;'
|
style='flex-grow: 1;'
|
||||||
:type='agendaStore.isTimezoneMeeting ? `primary` : `default`'
|
:type='agendaStore.isTimezoneMeeting ? `primary` : `default`'
|
||||||
|
@ -47,7 +47,7 @@ n-drawer(v-model:show='isShown', placement='right', :width='panelWidth')
|
||||||
:type='agendaStore.timezone === `UTC` ? `primary` : `default`'
|
:type='agendaStore.timezone === `UTC` ? `primary` : `default`'
|
||||||
@click='setTimezone(`UTC`)'
|
@click='setTimezone(`UTC`)'
|
||||||
) UTC
|
) UTC
|
||||||
n-select.mt-2(
|
n-select.mt-2#agenda-settings-tz-ddn(
|
||||||
v-model:value='agendaStore.timezone'
|
v-model:value='agendaStore.timezone'
|
||||||
:options='timezones'
|
:options='timezones'
|
||||||
placeholder='Select Time Zone'
|
placeholder='Select Time Zone'
|
||||||
|
@ -61,7 +61,7 @@ n-drawer(v-model:show='isShown', placement='right', :width='panelWidth')
|
||||||
//- .d-flex.align-items-center.mt-3
|
//- .d-flex.align-items-center.mt-3
|
||||||
//- n-switch.me-3(v-model:value='agendaStore.listDayCollapse', disabled)
|
//- n-switch.me-3(v-model:value='agendaStore.listDayCollapse', disabled)
|
||||||
//- span.small Collapse Days by Default
|
//- span.small Collapse Days by Default
|
||||||
.d-flex.align-items-center.mt-3
|
#agenda-settings-tgl-colorlgd.d-flex.align-items-center.mt-3
|
||||||
n-switch.me-3(
|
n-switch.me-3(
|
||||||
v-model:value='agendaStore.colorLegendShown'
|
v-model:value='agendaStore.colorLegendShown'
|
||||||
aria-label='Display Color Legend'
|
aria-label='Display Color Legend'
|
||||||
|
@ -71,7 +71,7 @@ n-drawer(v-model:show='isShown', placement='right', :width='panelWidth')
|
||||||
template(#trigger)
|
template(#trigger)
|
||||||
i.bi.bi-info-circle
|
i.bi.bi-info-circle
|
||||||
span Only displayed when a color is assigned to at least 1 event.
|
span Only displayed when a color is assigned to at least 1 event.
|
||||||
.d-flex.align-items-center.mt-3
|
#agenda-settings-tgl-infonote.d-flex.align-items-center.mt-3
|
||||||
n-switch.me-3(
|
n-switch.me-3(
|
||||||
v-model:value='agendaStore.infoNoteShown'
|
v-model:value='agendaStore.infoNoteShown'
|
||||||
aria-label='Display Current Meeting Info Note'
|
aria-label='Display Current Meeting Info Note'
|
||||||
|
@ -81,19 +81,19 @@ n-drawer(v-model:show='isShown', placement='right', :width='panelWidth')
|
||||||
template(#trigger)
|
template(#trigger)
|
||||||
i.bi.bi-info-circle
|
i.bi.bi-info-circle
|
||||||
span Any update to the note will result in this setting being turned back on.
|
span Any update to the note will result in this setting being turned back on.
|
||||||
.d-flex.align-items-center.mt-3
|
#agenda-settings-tgl-eventicons.d-flex.align-items-center.mt-3
|
||||||
n-switch.me-3(
|
n-switch.me-3(
|
||||||
v-model:value='agendaStore.eventIconsShown'
|
v-model:value='agendaStore.eventIconsShown'
|
||||||
aria-label='Display Event Icons'
|
aria-label='Display Event Icons'
|
||||||
)
|
)
|
||||||
span.small Display Event Icons
|
span.small Display Event Icons
|
||||||
.d-flex.align-items-center.mt-3
|
#agenda-settings-tgl-floorind.d-flex.align-items-center.mt-3
|
||||||
n-switch.me-3(
|
n-switch.me-3(
|
||||||
v-model:value='agendaStore.floorIndicatorsShown'
|
v-model:value='agendaStore.floorIndicatorsShown'
|
||||||
aria-label='Display Floor Indicators'
|
aria-label='Display Floor Indicators'
|
||||||
)
|
)
|
||||||
span.small Display Floor Indicators
|
span.small Display Floor Indicators
|
||||||
.d-flex.align-items-center.mt-3
|
#agenda-settings-tgl-groupind.d-flex.align-items-center.mt-3
|
||||||
n-switch.me-3(
|
n-switch.me-3(
|
||||||
v-model:value='agendaStore.areaIndicatorsShown'
|
v-model:value='agendaStore.areaIndicatorsShown'
|
||||||
aria-label='Display Group Area Indicators'
|
aria-label='Display Group Area Indicators'
|
||||||
|
@ -103,7 +103,7 @@ n-drawer(v-model:show='isShown', placement='right', :width='panelWidth')
|
||||||
template(#trigger)
|
template(#trigger)
|
||||||
i.bi.bi-info-circle
|
i.bi.bi-info-circle
|
||||||
span Will not be shown on smaller screens, regardless of this setting.
|
span Will not be shown on smaller screens, regardless of this setting.
|
||||||
.d-flex.align-items-center.mt-3
|
#agenda-settings-tgl-redline.d-flex.align-items-center.mt-3
|
||||||
n-switch.me-3(
|
n-switch.me-3(
|
||||||
v-model:value='agendaStore.redhandShown'
|
v-model:value='agendaStore.redhandShown'
|
||||||
aria-label='Display Realtime Red Line'
|
aria-label='Display Realtime Red Line'
|
||||||
|
@ -113,7 +113,7 @@ n-drawer(v-model:show='isShown', placement='right', :width='panelWidth')
|
||||||
template(#trigger)
|
template(#trigger)
|
||||||
i.bi.bi-info-circle
|
i.bi.bi-info-circle
|
||||||
span Only shown during live events. Updated every 5 seconds.
|
span Only shown during live events. Updated every 5 seconds.
|
||||||
.d-flex.align-items-center.mt-3
|
#agenda-settings-tgl-boldertxt.d-flex.align-items-center.mt-3
|
||||||
n-switch.me-3(
|
n-switch.me-3(
|
||||||
v-model:value='agendaStore.bolderText'
|
v-model:value='agendaStore.bolderText'
|
||||||
aria-label='Use Bolder Text'
|
aria-label='Use Bolder Text'
|
||||||
|
@ -140,7 +140,7 @@ n-drawer(v-model:show='isShown', placement='right', :width='panelWidth')
|
||||||
n-divider#agenda-settings-colors-header(title-placement='left')
|
n-divider#agenda-settings-colors-header(title-placement='left')
|
||||||
i.bi.bi-palette.me-2
|
i.bi.bi-palette.me-2
|
||||||
small Custom Colors / Tags
|
small Custom Colors / Tags
|
||||||
.d-flex.align-items-center.mt-3(v-for='(cl, idx) of state.colors')
|
.agenda-settings-colors-row.d-flex.align-items-center.mt-3(v-for='(cl, idx) of state.colors')
|
||||||
n-color-picker.me-3(
|
n-color-picker.me-3(
|
||||||
:modes='[`hex`]'
|
:modes='[`hex`]'
|
||||||
:render-label='() => {}'
|
:render-label='() => {}'
|
||||||
|
|
|
@ -216,10 +216,9 @@ onBeforeUnmount(() => {
|
||||||
// MOUNTED
|
// MOUNTED
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// -> Go to current meeting if not provided
|
agendaStore.fetch(route.params.meetingNumber)
|
||||||
if (!route.params.meetingNumber && agendaStore.meeting.number) {
|
|
||||||
router.replace({ params: { meetingNumber: agendaStore.meeting.number } })
|
handleCurrentMeetingRedirect()
|
||||||
}
|
|
||||||
|
|
||||||
// -> Hide Loading Screen
|
// -> Hide Loading Screen
|
||||||
if (agendaStore.isLoaded) {
|
if (agendaStore.isLoaded) {
|
||||||
|
|
|
@ -133,11 +133,14 @@ export const useAgendaStore = defineStore('agenda', {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
async fetch () {
|
async fetch (meetingNumber) {
|
||||||
try {
|
try {
|
||||||
const meetingData = JSON.parse(document.getElementById('meeting-data').textContent)
|
if (!meetingNumber) {
|
||||||
|
const meetingData = JSON.parse(document.getElementById('meeting-data').textContent)
|
||||||
|
meetingNumber = meetingData.meetingNumber
|
||||||
|
}
|
||||||
|
|
||||||
const resp = await fetch(`/api/meeting/${meetingData.meetingNumber}/agenda-data`, { credentials: 'omit' })
|
const resp = await fetch(`/api/meeting/${meetingNumber}/agenda-data`, { credentials: 'omit' })
|
||||||
if (!resp.ok) {
|
if (!resp.ok) {
|
||||||
throw new Error(resp.statusText)
|
throw new Error(resp.statusText)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,94 +0,0 @@
|
||||||
describe('meeting agenda', () => {
|
|
||||||
before(() => {
|
|
||||||
cy.visit('/meeting/113/agenda/')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('toggle customize panel when clicking on customize header bar', () => {
|
|
||||||
cy.get('#agenda-filter-customize').click()
|
|
||||||
cy.get('#customize').should('be.visible').and('have.class', 'show')
|
|
||||||
|
|
||||||
cy.get('#agenda-filter-customize').click()
|
|
||||||
cy.get('#customize').should('not.be.visible').and('not.have.class', 'show')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('customize panel should have at least 3 areas', () => {
|
|
||||||
cy.get('#agenda-filter-customize').click()
|
|
||||||
cy.get('.agenda-filter-areaselectbtn').should('have.length.at.least', 3)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('customize panel should have at least 10 groups', () => {
|
|
||||||
cy.get('.agenda-filter-groupselectbtn').should('have.length.at.least', 10)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('filtering the agenda should modify the URL', () => {
|
|
||||||
cy.get('.agenda-filter-groupselectbtn').take(5).as('selectedGroups').each(randomElement => {
|
|
||||||
cy.wrap(randomElement).click()
|
|
||||||
cy.wrap(randomElement).invoke('attr', 'data-filter-item').then(keyword => {
|
|
||||||
cy.url().should('contain', keyword)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// Deselect everything
|
|
||||||
cy.get('@selectedGroups').click({ multiple: true })
|
|
||||||
})
|
|
||||||
|
|
||||||
it('selecting an area should select all corresponding groups', () => {
|
|
||||||
cy.get('.agenda-filter-areaselectbtn').first().click().invoke('attr', 'data-filter-item').then(area => {
|
|
||||||
cy.url().should('contain', area)
|
|
||||||
|
|
||||||
cy.get(`.agenda-filter-groupselectbtn[data-filter-keywords*="${area}"]`).each(group => {
|
|
||||||
cy.wrap(group).invoke('attr', 'data-filter-keywords').then(groupKeywords => {
|
|
||||||
// In case value is a comma-separated list of keywords...
|
|
||||||
if (groupKeywords.indexOf(',') < 0 || groupKeywords.split(',').includes(area)) {
|
|
||||||
cy.wrap(group).should('have.class', 'active')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('weekview iframe should load', () => {
|
|
||||||
cy.get('#weekview > iframe').its('0.contentDocument').should('exist')
|
|
||||||
cy.get('#weekview > iframe').its('0.contentDocument.readyState').should('equal', 'complete')
|
|
||||||
cy.get('#weekview > iframe').its('0.contentDocument.body', {
|
|
||||||
timeout: 30000
|
|
||||||
}).should('not.be.empty')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('meeting agenda weekview', () => {
|
|
||||||
before(() => {
|
|
||||||
cy.visit('/meeting/113/agenda/week-view.html')
|
|
||||||
})
|
|
||||||
it('should have day headers', () => {
|
|
||||||
cy.get('.agenda-weekview-day').should('have.length.greaterThan', 0).and('be.visible')
|
|
||||||
})
|
|
||||||
it('should have day columns', () => {
|
|
||||||
cy.get('.agenda-weekview-column').should('have.length.greaterThan', 0).and('be.visible')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should have the same number of day headers and columns', () => {
|
|
||||||
cy.get('.agenda-weekview-day').its('length').then(lgth => {
|
|
||||||
cy.get('.agenda-weekview-column').should('have.length', lgth)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should have meetings', () => {
|
|
||||||
cy.get('.agenda-weekview-meeting').should('have.length.greaterThan', 0).and('be.visible')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('meeting hover should cause expansion to column width', () => {
|
|
||||||
cy.get('.agenda-weekview-column:first').invoke('outerWidth').then(colWidth => {
|
|
||||||
/* eslint-disable cypress/no-unnecessary-waiting */
|
|
||||||
cy.get('.agenda-weekview-meeting-mini').any(5).each(meeting => {
|
|
||||||
cy.wrap(meeting)
|
|
||||||
.wait(250)
|
|
||||||
.realHover({ position: 'center' })
|
|
||||||
.invoke('outerWidth')
|
|
||||||
.should('be.closeTo', colWidth, 1)
|
|
||||||
// Move over to top left corner of the page to end the mouseover of the current meeting block
|
|
||||||
cy.get('.agenda-weekview-day:first').realHover().wait(250)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -7,7 +7,7 @@ export default function servePreviewAssets () {
|
||||||
name: 'serve-preview-assets',
|
name: 'serve-preview-assets',
|
||||||
configurePreviewServer(server) {
|
configurePreviewServer(server) {
|
||||||
server.middlewares.use('/media/floor', (req, res, next) => {
|
server.middlewares.use('/media/floor', (req, res, next) => {
|
||||||
send(req, url.parse(req.url).pathname, { root: path.join(process.cwd(), 'cypress/fixtures/floor-plan-images') }).pipe(res)
|
send(req, url.parse(req.url).pathname, { root: path.join(process.cwd(), 'playwright/data/floor-plan-images') }).pipe(res)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,6 +67,7 @@
|
||||||
"eslint-config-standard": "17.0.0",
|
"eslint-config-standard": "17.0.0",
|
||||||
"eslint-plugin-cypress": "2.12.1",
|
"eslint-plugin-cypress": "2.12.1",
|
||||||
"eslint-plugin-import": "2.26.0",
|
"eslint-plugin-import": "2.26.0",
|
||||||
|
"eslint-plugin-n": "15.2.5",
|
||||||
"eslint-plugin-node": "11.1.0",
|
"eslint-plugin-node": "11.1.0",
|
||||||
"eslint-plugin-promise": "6.0.1",
|
"eslint-plugin-promise": "6.0.1",
|
||||||
"eslint-plugin-vue": "9.4.0",
|
"eslint-plugin-vue": "9.4.0",
|
||||||
|
|
6
playwright/.editorconfig
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
[*.js]
|
||||||
|
indent_size = 2
|
||||||
|
indent_style = space
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
16
playwright/.eslintrc.js
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
module.exports = {
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"commonjs": true,
|
||||||
|
"es2021": true,
|
||||||
|
"node": true
|
||||||
|
},
|
||||||
|
"extends": "standard",
|
||||||
|
"overrides": [
|
||||||
|
],
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": "latest"
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
}
|
||||||
|
}
|
4
playwright/.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
node_modules/
|
||||||
|
/test-results/
|
||||||
|
/playwright-report/
|
||||||
|
/playwright/.cache/
|
57
playwright/README.md
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
# Playwright
|
||||||
|
##### Frontend testing automation tool
|
||||||
|
|
||||||
|
- [Playwright Website](https://playwright.dev/)
|
||||||
|
- [Playwright Docs](https://playwright.dev/docs/intro)
|
||||||
|
- [Playwright API Reference](https://playwright.dev/docs/api/class-test)
|
||||||
|
- [Online Trace Viewer](https://trace.playwright.dev/)
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
Make sure you run all commands from the `/playwright` directory, not the project root.
|
||||||
|
|
||||||
|
```
|
||||||
|
npm install
|
||||||
|
npx playwright install --with-deps
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Running all tests headless:
|
||||||
|
```
|
||||||
|
npm test
|
||||||
|
```
|
||||||
|
|
||||||
|
Running all tests serially in visual mode (headed):
|
||||||
|
```
|
||||||
|
npm run test:visual
|
||||||
|
```
|
||||||
|
|
||||||
|
Running all tests in debug mode:
|
||||||
|
```
|
||||||
|
npm run test:debug
|
||||||
|
```
|
||||||
|
|
||||||
|
## Advanced Usage
|
||||||
|
|
||||||
|
> Refer to the [CLI Reference](https://playwright.dev/docs/test-cli#reference) for all possible options.
|
||||||
|
|
||||||
|
Running a single test file:
|
||||||
|
```
|
||||||
|
npx playwright test foo.spec.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
Running test files that have `foo` or `bar` in the filename:
|
||||||
|
```
|
||||||
|
npx playwright test foo bar
|
||||||
|
```
|
||||||
|
|
||||||
|
Running tests in a specific browser *(e.g. chromium)*:
|
||||||
|
```
|
||||||
|
npx playwright test --project=chromium
|
||||||
|
```
|
||||||
|
|
||||||
|
Running tests in headed mode:
|
||||||
|
```
|
||||||
|
npx playwright test --headed
|
||||||
|
```
|
Before Width: | Height: | Size: 320 KiB After Width: | Height: | Size: 320 KiB |
Before Width: | Height: | Size: 198 KiB After Width: | Height: | Size: 198 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 119 KiB After Width: | Height: | Size: 119 KiB |
Before Width: | Height: | Size: 139 KiB After Width: | Height: | Size: 139 KiB |
Before Width: | Height: | Size: 492 KiB After Width: | Height: | Size: 492 KiB |
17
playwright/helpers/common.js
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
module.exports = {
|
||||||
|
/**
|
||||||
|
* Validate whether a selector is visible in viewport
|
||||||
|
*
|
||||||
|
* @param {Object} page Page object
|
||||||
|
* @param {String} selector Selector to validate
|
||||||
|
* @returns Boolean
|
||||||
|
*/
|
||||||
|
isIntersectingViewport: async (page, selector) => {
|
||||||
|
return page.$eval(selector, async el => {
|
||||||
|
const bottom = window.innerHeight
|
||||||
|
const rect = el.getBoundingClientRect()
|
||||||
|
|
||||||
|
return rect.top < bottom && rect.top > 0 - rect.height
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +1,14 @@
|
||||||
import { DateTime } from 'luxon'
|
const { DateTime } = require('luxon')
|
||||||
import { faker } from '@faker-js/faker'
|
const { faker } = require('@faker-js/faker')
|
||||||
import seedrandom from 'seedrandom'
|
const seedrandom = require('seedrandom')
|
||||||
import _lodash from 'lodash' // Cannot use lodash-es as we need to runInContext for constant randomness
|
const _ = require('lodash')
|
||||||
import { startCase, times } from 'lodash-es'
|
const slugify = require('slugify')
|
||||||
import slugify from 'slugify'
|
const ms = require('ms')
|
||||||
import ms from 'ms'
|
|
||||||
|
|
||||||
import floorsMeta from '../fixtures/meeting-floors.json'
|
const floorsMeta = require('../data/meeting-floors')
|
||||||
|
|
||||||
|
const urlRe = /http[s]?:\/\/(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*(),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+/
|
||||||
|
const conferenceDomains = ['webex.com', 'zoom.us', 'jitsi.org', 'meetecho.com', 'gather.town']
|
||||||
|
|
||||||
const xslugify = (str) => slugify(str.replace('/', '-'), { lower: true, strict: true })
|
const xslugify = (str) => slugify(str.replace('/', '-'), { lower: true, strict: true })
|
||||||
|
|
||||||
|
@ -20,7 +22,7 @@ const sessionsWithWebex = [3, 4]
|
||||||
// Use constant randomness seed
|
// Use constant randomness seed
|
||||||
seedrandom(TEST_SEED.toString(), { global: true })
|
seedrandom(TEST_SEED.toString(), { global: true })
|
||||||
faker.seed(TEST_SEED)
|
faker.seed(TEST_SEED)
|
||||||
const { sample, sampleSize } = _lodash.runInContext()
|
const { sample, sampleSize } = _.runInContext()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate area response from label + children
|
* Generate area response from label + children
|
||||||
|
@ -85,7 +87,6 @@ function findAreaGroup (slug, areas) {
|
||||||
throw new Error('Requested group does not exist!')
|
throw new Error('Requested group does not exist!')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reverse areas and groups mapping
|
* Reverse areas and groups mapping
|
||||||
*/
|
*/
|
||||||
|
@ -142,10 +143,12 @@ function createEvent ({
|
||||||
id: ++lastEventId,
|
id: ++lastEventId,
|
||||||
sessionId: ++lastSessionId,
|
sessionId: ++lastSessionId,
|
||||||
room: room.name,
|
room: room.name,
|
||||||
location: hasLocation ? {
|
location: hasLocation
|
||||||
short: floor.short,
|
? {
|
||||||
name: floor.name
|
short: floor.short,
|
||||||
} : {},
|
name: floor.name
|
||||||
|
}
|
||||||
|
: {},
|
||||||
acronym: group.keyword,
|
acronym: group.keyword,
|
||||||
duration: typeof duration === 'string' ? ms(duration) / 1000 : duration,
|
duration: typeof duration === 'string' ? ms(duration) / 1000 : duration,
|
||||||
name: eventName,
|
name: eventName,
|
||||||
|
@ -154,9 +157,9 @@ function createEvent ({
|
||||||
type,
|
type,
|
||||||
isBoF,
|
isBoF,
|
||||||
filterKeywords: [
|
filterKeywords: [
|
||||||
"coding",
|
'coding',
|
||||||
"hackathon",
|
'hackathon',
|
||||||
"hackathon-sessc"
|
'hackathon-sessc'
|
||||||
],
|
],
|
||||||
groupAcronym: group.keyword,
|
groupAcronym: group.keyword,
|
||||||
groupName: faker.lorem.sentence(faker.mersenne.rand(5, 2)),
|
groupName: faker.lorem.sentence(faker.mersenne.rand(5, 2)),
|
||||||
|
@ -170,22 +173,24 @@ function createEvent ({
|
||||||
showAgenda
|
showAgenda
|
||||||
},
|
},
|
||||||
agenda: {
|
agenda: {
|
||||||
url: hasAgenda ? `https://datatracker.ietf.org/meeting/123/materials/agenda-123-ietf-sessa-00` : null
|
url: hasAgenda ? 'https://datatracker.ietf.org/meeting/123/materials/agenda-123-ietf-sessa-00' : null
|
||||||
},
|
},
|
||||||
orderInMeeting: 1,
|
orderInMeeting: 1,
|
||||||
short: eventName,
|
short: eventName,
|
||||||
sessionToken: "sessa",
|
sessionToken: 'sessa',
|
||||||
links: {
|
links: {
|
||||||
chat: `https://zulip.ietf.org/#narrow/stream/${group.keyword}`,
|
chat: `https://zulip.ietf.org/#narrow/stream/${group.keyword}`,
|
||||||
chatArchive: `https://zulip.ietf.org/#narrow/stream/${group.keyword}`,
|
chatArchive: `https://zulip.ietf.org/#narrow/stream/${group.keyword}`,
|
||||||
recordings: hasRecordings ? [
|
recordings: hasRecordings
|
||||||
{
|
? [
|
||||||
id: ++lastRecordingId,
|
{
|
||||||
name: `recording-123-${group.keyword}-1`,
|
id: ++lastRecordingId,
|
||||||
title: `Video recording for ${group.keyword} on ${startDateTime.toFormat('yyyy-LL-dd \'at\' HH:mm:ss')}`,
|
name: `recording-123-${group.keyword}-1`,
|
||||||
url: "https://www.youtube.com/watch?v=1eq_5xvacl0"
|
title: `Video recording for ${group.keyword} on ${startDateTime.toFormat('yyyy-LL-dd \'at\' HH:mm:ss')}`,
|
||||||
}
|
url: 'https://www.youtube.com/watch?v=1eq_5xvacl0'
|
||||||
] : [],
|
}
|
||||||
|
]
|
||||||
|
: [],
|
||||||
videoStream: showAgenda && hasVideoStream ? 'https://meetings.conf.meetecho.com/ietf{meeting.number}/?group={group.acronym}&short={short}&item={order_number}' : null,
|
videoStream: showAgenda && hasVideoStream ? 'https://meetings.conf.meetecho.com/ietf{meeting.number}/?group={group.acronym}&short={short}&item={order_number}' : null,
|
||||||
audioStream: hasAgenda ? 'https://mp3.conf.meetecho.com/ietf123/{group.acronym}/{order_number}.m3u' : null,
|
audioStream: hasAgenda ? 'https://mp3.conf.meetecho.com/ietf123/{group.acronym}/{order_number}.m3u' : null,
|
||||||
webex: hasWebex ? 'https://webex.com/123' : null,
|
webex: hasWebex ? 'https://webex.com/123' : null,
|
||||||
|
@ -195,7 +200,7 @@ function createEvent ({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
module.exports = {
|
||||||
/**
|
/**
|
||||||
* Generate a standard agenda data reponse
|
* Generate a standard agenda data reponse
|
||||||
*/
|
*/
|
||||||
|
@ -219,17 +224,17 @@ export default {
|
||||||
const endDate = startDate.plus({ days: 7 })
|
const endDate = startDate.plus({ days: 7 })
|
||||||
|
|
||||||
// Generate floors
|
// Generate floors
|
||||||
const floors = times(6, (idx) => {
|
const floors = _.times(6, (idx) => {
|
||||||
const floorIdx = idx + 1
|
const floorIdx = idx + 1
|
||||||
const floor = floorsMeta[idx]
|
const floor = floorsMeta[idx]
|
||||||
return {
|
return {
|
||||||
id: floorIdx,
|
id: floorIdx,
|
||||||
image: `/media/floor/${floor.path}`,
|
image: `/media/floor/${floor.path}`,
|
||||||
name: `Level ${startCase(faker.color.human())} ${floorIdx}`,
|
name: `Level ${_.startCase(faker.color.human())} ${floorIdx}`,
|
||||||
short: `L${floorIdx}`,
|
short: `L${floorIdx}`,
|
||||||
width: floor.width,
|
width: floor.width,
|
||||||
height: floor.height,
|
height: floor.height,
|
||||||
rooms: times(faker.mersenne.rand(10, 5), (ridx) => {
|
rooms: _.times(faker.mersenne.rand(10, 5), (ridx) => {
|
||||||
const roomName = `${faker.science.chemicalElement().name} ${floorIdx}-${ridx + 1}`
|
const roomName = `${faker.science.chemicalElement().name} ${floorIdx}-${ridx + 1}`
|
||||||
// Keep 10% margin on each side
|
// Keep 10% margin on each side
|
||||||
const roomXUnit = Math.round(floor.width / 10)
|
const roomXUnit = Math.round(floor.width / 10)
|
||||||
|
@ -239,7 +244,7 @@ export default {
|
||||||
return {
|
return {
|
||||||
id: floorIdx * 100 + ridx,
|
id: floorIdx * 100 + ridx,
|
||||||
name: roomName,
|
name: roomName,
|
||||||
functionalName: startCase(faker.lorem.words(2)),
|
functionalName: _.startCase(faker.lorem.words(2)),
|
||||||
slug: xslugify(roomName),
|
slug: xslugify(roomName),
|
||||||
left: roomX,
|
left: roomX,
|
||||||
right: roomX + roomXUnit,
|
right: roomX + roomXUnit,
|
||||||
|
@ -262,7 +267,7 @@ export default {
|
||||||
for (const area of firstAreasNames) {
|
for (const area of firstAreasNames) {
|
||||||
firstAreas.push(createArea({
|
firstAreas.push(createArea({
|
||||||
label: area,
|
label: area,
|
||||||
children: times(faker.mersenne.rand(25, 2), (idx) => {
|
children: _.times(faker.mersenne.rand(25, 2), (idx) => {
|
||||||
return createGroup({ mayBeBof: true })
|
return createGroup({ mayBeBof: true })
|
||||||
})
|
})
|
||||||
}))
|
}))
|
||||||
|
@ -275,7 +280,7 @@ export default {
|
||||||
for (const area of ['UVW', 'XYZ0']) {
|
for (const area of ['UVW', 'XYZ0']) {
|
||||||
secondAreas.push(createArea({
|
secondAreas.push(createArea({
|
||||||
label: area,
|
label: area,
|
||||||
children: times(faker.mersenne.rand(25, 2), (idx) => {
|
children: _.times(faker.mersenne.rand(25, 2), (idx) => {
|
||||||
return createGroup({ mayBeBof: true })
|
return createGroup({ mayBeBof: true })
|
||||||
})
|
})
|
||||||
}))
|
}))
|
||||||
|
@ -301,13 +306,13 @@ export default {
|
||||||
}),
|
}),
|
||||||
createArea({
|
createArea({
|
||||||
label: 'Office hours',
|
label: 'Office hours',
|
||||||
children: firstAreasNames.map(n => createGroup({ label: `${n} Office Hours`}))
|
children: firstAreasNames.map(n => createGroup({ label: `${n} Office Hours` }))
|
||||||
}),
|
}),
|
||||||
createArea({
|
createArea({
|
||||||
label: 'Open meeting',
|
label: 'Open meeting',
|
||||||
children: [
|
children: [
|
||||||
createGroup({ label: 'WG Chairs Forum' }),
|
createGroup({ label: 'WG Chairs Forum' }),
|
||||||
createGroup({ label: `Newcomers' Feedback Session` })
|
createGroup({ label: 'Newcomers\' Feedback Session' })
|
||||||
]
|
]
|
||||||
}),
|
}),
|
||||||
createArea({
|
createArea({
|
||||||
|
@ -327,7 +332,7 @@ export default {
|
||||||
createArea({
|
createArea({
|
||||||
label: 'Social',
|
label: 'Social',
|
||||||
children: [
|
children: [
|
||||||
createGroup({ label: `Newcomers' Quick Connections` }),
|
createGroup({ label: 'Newcomers\' Quick Connections' }),
|
||||||
createGroup({ label: 'Welcome Reception', toggledBy: ['ietf'] }),
|
createGroup({ label: 'Welcome Reception', toggledBy: ['ietf'] }),
|
||||||
createGroup({ label: 'Break', toggledBy: ['secretariat'] }),
|
createGroup({ label: 'Break', toggledBy: ['secretariat'] }),
|
||||||
createGroup({ label: 'Beverage and Snack Break', toggledBy: ['secretariat'] }),
|
createGroup({ label: 'Beverage and Snack Break', toggledBy: ['secretariat'] }),
|
||||||
|
@ -337,7 +342,7 @@ export default {
|
||||||
createArea({
|
createArea({
|
||||||
label: 'Tutorial',
|
label: 'Tutorial',
|
||||||
children: [
|
children: [
|
||||||
createGroup({ label: `Tutorial: Newcomers' Overview` })
|
createGroup({ label: 'Tutorial: Newcomers\' Overview' })
|
||||||
]
|
]
|
||||||
}),
|
}),
|
||||||
createArea({
|
createArea({
|
||||||
|
@ -434,7 +439,7 @@ export default {
|
||||||
}, floors))
|
}, floors))
|
||||||
|
|
||||||
schedule.push(createEvent({
|
schedule.push(createEvent({
|
||||||
name: `Newcomers' Quick Connections (Note that pre-registration is required)`,
|
name: 'Newcomers\' Quick Connections (Note that pre-registration is required)',
|
||||||
startDateTime: day2.set({ hour: 16 }),
|
startDateTime: day2.set({ hour: 16 }),
|
||||||
duration: '1h',
|
duration: '1h',
|
||||||
...findAreaGroup('newcomers-quick-connections', categories[2]),
|
...findAreaGroup('newcomers-quick-connections', categories[2]),
|
||||||
|
@ -506,7 +511,7 @@ export default {
|
||||||
}, floors))
|
}, floors))
|
||||||
|
|
||||||
// -> Session I
|
// -> Session I
|
||||||
times(8, () => { // 8 lanes per session time
|
_.times(8, () => { // 8 lanes per session time
|
||||||
const { area, ...group } = daySessions.pop()
|
const { area, ...group } = daySessions.pop()
|
||||||
schedule.push(createEvent({
|
schedule.push(createEvent({
|
||||||
name: 'Session I',
|
name: 'Session I',
|
||||||
|
@ -535,7 +540,7 @@ export default {
|
||||||
}, floors))
|
}, floors))
|
||||||
|
|
||||||
// -> Session II
|
// -> Session II
|
||||||
times(8, () => { // 8 lanes per session time
|
_.times(8, () => { // 8 lanes per session time
|
||||||
const { area, ...group } = daySessions.pop()
|
const { area, ...group } = daySessions.pop()
|
||||||
schedule.push(createEvent({
|
schedule.push(createEvent({
|
||||||
name: 'Session II',
|
name: 'Session II',
|
||||||
|
@ -564,9 +569,9 @@ export default {
|
||||||
type: 'break',
|
type: 'break',
|
||||||
...findAreaGroup('beverage-and-snack-break', categories[2])
|
...findAreaGroup('beverage-and-snack-break', categories[2])
|
||||||
}, floors))
|
}, floors))
|
||||||
|
|
||||||
// -> Session III
|
// -> Session III
|
||||||
times(8, () => { // 8 lanes per session time
|
_.times(8, () => { // 8 lanes per session time
|
||||||
const { area, ...group } = daySessions.pop()
|
const { area, ...group } = daySessions.pop()
|
||||||
schedule.push(createEvent({
|
schedule.push(createEvent({
|
||||||
name: 'Session III',
|
name: 'Session III',
|
||||||
|
@ -627,5 +632,41 @@ export default {
|
||||||
schedule,
|
schedule,
|
||||||
floors
|
floors
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format URL by replacing inline variables
|
||||||
|
*
|
||||||
|
* @param {String} url Raw URL
|
||||||
|
* @param {Object} session Session Object
|
||||||
|
* @param {String} meetingNumber Meeting Number
|
||||||
|
* @returns Formatted URL
|
||||||
|
*/
|
||||||
|
formatLinkUrl: (url, session, meetingNumber) => {
|
||||||
|
return url
|
||||||
|
? url.replace('{meeting.number}', meetingNumber)
|
||||||
|
.replace('{group.acronym}', session.groupAcronym)
|
||||||
|
.replace('{short}', session.short)
|
||||||
|
.replace('{order_number}', session.orderInMeeting)
|
||||||
|
: url
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the first URL in text matching a conference domain
|
||||||
|
*
|
||||||
|
* @param {String} txt Raw Text
|
||||||
|
* @returns First URL found
|
||||||
|
*/
|
||||||
|
findFirstConferenceUrl: (txt) => {
|
||||||
|
try {
|
||||||
|
const fUrl = txt.match(urlRe)
|
||||||
|
if (fUrl && fUrl[0].length > 0) {
|
||||||
|
const pUrl = new URL(fUrl[0])
|
||||||
|
if (conferenceDomains.some(d => pUrl.hostname.endsWith(d))) {
|
||||||
|
return fUrl[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) { }
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
6
playwright/helpers/viewports.js
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
module.exports = {
|
||||||
|
desktop: [1536, 960],
|
||||||
|
smallDesktop: [1280, 800],
|
||||||
|
tablet: [768, 1024],
|
||||||
|
mobile: [360, 760]
|
||||||
|
}
|
4015
playwright/package-lock.json
generated
Normal file
32
playwright/package.json
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
{
|
||||||
|
"name": "playwright",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "playwright test",
|
||||||
|
"test:visual": "playwright test --headed --workers=1",
|
||||||
|
"test:debug": "playwright test --debug"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"devDependencies": {
|
||||||
|
"@playwright/test": "^1.25.2",
|
||||||
|
"eslint": "^8.23.1",
|
||||||
|
"eslint-config-standard": "^17.0.0",
|
||||||
|
"eslint-plugin-import": "^2.26.0",
|
||||||
|
"eslint-plugin-n": "^15.2.5",
|
||||||
|
"eslint-plugin-node": "^11.1.0",
|
||||||
|
"eslint-plugin-promise": "^6.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@faker-js/faker": "^7.5.0",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
|
"lodash-es": "^4.17.21",
|
||||||
|
"luxon": "^3.0.3",
|
||||||
|
"ms": "^2.1.3",
|
||||||
|
"seedrandom": "^3.0.5",
|
||||||
|
"slugify": "^1.6.5"
|
||||||
|
}
|
||||||
|
}
|
116
playwright/playwright.config.js
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
// @ts-check
|
||||||
|
const { devices } = require('@playwright/test')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read environment variables from file.
|
||||||
|
* https://github.com/motdotla/dotenv
|
||||||
|
*/
|
||||||
|
// require('dotenv').config();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://playwright.dev/docs/test-configuration
|
||||||
|
* @type {import('@playwright/test').PlaywrightTestConfig}
|
||||||
|
*/
|
||||||
|
const config = {
|
||||||
|
testDir: './tests',
|
||||||
|
/* Maximum time one test can run for. */
|
||||||
|
timeout: 120 * 1000,
|
||||||
|
expect: {
|
||||||
|
/**
|
||||||
|
* Maximum time expect() should wait for the condition to be met.
|
||||||
|
* For example in `await expect(locator).toHaveText();`
|
||||||
|
*/
|
||||||
|
timeout: 5000
|
||||||
|
},
|
||||||
|
/* Run tests in files in parallel */
|
||||||
|
fullyParallel: true,
|
||||||
|
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||||
|
forbidOnly: !!process.env.CI,
|
||||||
|
/* Retry on CI only */
|
||||||
|
retries: process.env.CI ? 2 : 0,
|
||||||
|
/* Opt out of parallel tests on CI. */
|
||||||
|
workers: process.env.CI ? 1 : undefined,
|
||||||
|
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||||
|
reporter: process.env.CI ? 'github' : 'list',
|
||||||
|
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||||
|
use: {
|
||||||
|
/* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
|
||||||
|
actionTimeout: 0,
|
||||||
|
/* Base URL to use in actions like `await page.goto('/')`. */
|
||||||
|
baseURL: 'http://localhost:3000',
|
||||||
|
|
||||||
|
locale: 'en-US',
|
||||||
|
timezoneId: 'America/Toronto',
|
||||||
|
|
||||||
|
screenshot: 'only-on-failure',
|
||||||
|
|
||||||
|
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||||
|
trace: 'on-first-retry'
|
||||||
|
},
|
||||||
|
|
||||||
|
/* Configure projects for major browsers */
|
||||||
|
projects: [
|
||||||
|
{
|
||||||
|
name: 'chromium',
|
||||||
|
use: {
|
||||||
|
...devices['Desktop Chrome']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: 'firefox',
|
||||||
|
use: {
|
||||||
|
...devices['Desktop Firefox']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// {
|
||||||
|
// name: 'webkit',
|
||||||
|
// use: {
|
||||||
|
// ...devices['Desktop Safari']
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
/* Test against mobile viewports. */
|
||||||
|
// {
|
||||||
|
// name: 'Mobile Chrome',
|
||||||
|
// use: {
|
||||||
|
// ...devices['Pixel 5'],
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// name: 'Mobile Safari',
|
||||||
|
// use: {
|
||||||
|
// ...devices['iPhone 12'],
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
|
||||||
|
/* Test against branded browsers. */
|
||||||
|
// {
|
||||||
|
// name: 'Microsoft Edge',
|
||||||
|
// use: {
|
||||||
|
// channel: 'msedge',
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// name: 'Google Chrome',
|
||||||
|
// use: {
|
||||||
|
// channel: 'chrome',
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
],
|
||||||
|
|
||||||
|
/* Folder for test artifacts such as screenshots, videos, traces, etc. */
|
||||||
|
// outputDir: 'test-results/',
|
||||||
|
|
||||||
|
/* Run your local dev server before starting the tests */
|
||||||
|
...process.env.CI && {
|
||||||
|
webServer: {
|
||||||
|
command: 'cd .. && yarn preview',
|
||||||
|
port: 3000,
|
||||||
|
reuseExistingServer: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = config
|
1427
playwright/tests/meeting/agenda.spec.js
Normal file
129
playwright/tests/meeting/floor-plan.spec.js
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
const { test, expect } = require('@playwright/test')
|
||||||
|
const { faker } = require('@faker-js/faker')
|
||||||
|
const seedrandom = require('seedrandom')
|
||||||
|
const meetingGenerator = require('../../helpers/meeting.js')
|
||||||
|
const viewports = require('../../helpers/viewports')
|
||||||
|
const { setTimeout } = require('timers/promises')
|
||||||
|
|
||||||
|
const TEST_SEED = 123
|
||||||
|
|
||||||
|
// Set randomness seed
|
||||||
|
seedrandom(TEST_SEED.toString(), { global: true })
|
||||||
|
faker.seed(TEST_SEED)
|
||||||
|
|
||||||
|
// ====================================================================
|
||||||
|
// FLOOR-PLAN-NEUE | All Viewports
|
||||||
|
// ====================================================================
|
||||||
|
|
||||||
|
test.describe('floor-plan', () => {
|
||||||
|
let meetingData
|
||||||
|
|
||||||
|
test.beforeAll(async () => {
|
||||||
|
// Generate meeting data (without schedule data)
|
||||||
|
meetingData = meetingGenerator.generateAgendaResponse({ dateMode: 'past', skipSchedule: true })
|
||||||
|
})
|
||||||
|
|
||||||
|
for (const vp of ['desktop', 'smallDesktop', 'tablet', 'mobile']) {
|
||||||
|
test(vp, async ({ page }) => {
|
||||||
|
// Intercept Meeting Data API
|
||||||
|
await page.route(`**/api/meeting/${meetingData.meeting.number}/agenda-data`, route => {
|
||||||
|
route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: 'application/json',
|
||||||
|
body: JSON.stringify(meetingData)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.setViewportSize({
|
||||||
|
width: viewports[vp][0],
|
||||||
|
height: viewports[vp][1]
|
||||||
|
})
|
||||||
|
|
||||||
|
// Visit floor plan page and await Meeting Data API call to complete
|
||||||
|
await Promise.all([
|
||||||
|
page.waitForResponse(`**/api/meeting/${meetingData.meeting.number}/agenda-data`),
|
||||||
|
page.goto(`/meeting/${meetingData.meeting.number}/floor-plan-neue`)
|
||||||
|
])
|
||||||
|
|
||||||
|
// Wait for page to be ready
|
||||||
|
await page.locator('.floorplan h1').waitFor({ state: 'visible' })
|
||||||
|
await setTimeout(500)
|
||||||
|
|
||||||
|
// -> HEADER
|
||||||
|
|
||||||
|
await test.step(`has IETF ${meetingData.meeting.number} title`, async () => {
|
||||||
|
await expect(page.locator('.floorplan h1').first()).toContainText(`IETF ${meetingData.meeting.number} Floor Plan`)
|
||||||
|
})
|
||||||
|
await test.step('has meeting city subtitle', async () => {
|
||||||
|
await expect(page.locator('.floorplan h4').first()).toContainText(meetingData.meeting.city)
|
||||||
|
})
|
||||||
|
await test.step('has meeting date subtitle', async () => {
|
||||||
|
await expect(page.locator('.floorplan h4').first()).toContainText(/[a-zA-Z] [0-9]{1,2} - ([a-zA-Z]+ )?[0-9]{1,2}, [0-9]{4}/i)
|
||||||
|
})
|
||||||
|
|
||||||
|
// -> NAV
|
||||||
|
|
||||||
|
await test.step('has the correct navigation items', async () => {
|
||||||
|
const navItemsLocator = page.locator('.floorplan .meeting-nav > li')
|
||||||
|
await expect(navItemsLocator).toHaveCount(3)
|
||||||
|
await expect(navItemsLocator.first()).toContainText('Agenda')
|
||||||
|
await expect(navItemsLocator.nth(1)).toContainText('Floor plan')
|
||||||
|
await expect(navItemsLocator.last()).toContainText('Plaintext')
|
||||||
|
})
|
||||||
|
|
||||||
|
// -> FLOORS
|
||||||
|
|
||||||
|
await test.step('can switch between floors', async () => {
|
||||||
|
const floorsLocator = page.locator('.floorplan .floorplan-floors > .nav-link')
|
||||||
|
const floorImageLocator = page.locator('.floorplan .floorplan-plan > img')
|
||||||
|
|
||||||
|
await expect(floorsLocator).toHaveCount(meetingData.floors.length)
|
||||||
|
for (let idx = 0; idx < meetingData.floors.length; idx++) {
|
||||||
|
await expect(floorsLocator.nth(idx)).toContainText(meetingData.floors[idx].name)
|
||||||
|
await floorsLocator.nth(idx).click()
|
||||||
|
await expect(floorsLocator.nth(idx)).toHaveClass(/active/)
|
||||||
|
await expect(page.locator('.floorplan .floorplan-floors > .nav-link:not(.active)')).toHaveCount(meetingData.floors.length - 1)
|
||||||
|
// Wait for image to load + verify
|
||||||
|
await expect(floorImageLocator).toBeVisible()
|
||||||
|
await setTimeout(100)
|
||||||
|
await expect(await floorImageLocator.evaluate(node => node.naturalWidth)).toBeGreaterThan(1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// -> ROOMS
|
||||||
|
|
||||||
|
await test.step('can select rooms', async () => {
|
||||||
|
const roomsLocator = page.locator('.floorplan .floorplan-rooms > .list-group-item')
|
||||||
|
const floorImageLocator = page.locator('.floorplan .floorplan-plan > img')
|
||||||
|
const pinLocator = page.locator('.floorplan .floorplan-plan-pin')
|
||||||
|
const floor = meetingData.floors[0]
|
||||||
|
await page.locator('.floorplan .floorplan-floors > .nav-link').first().click()
|
||||||
|
await expect(roomsLocator).toHaveCount(floor.rooms.length)
|
||||||
|
for (let idx = 0; idx < floor.rooms.length; idx++) {
|
||||||
|
// Room List
|
||||||
|
const room = floor.rooms[idx]
|
||||||
|
await expect(roomsLocator.nth(idx).locator('strong')).toContainText(room.name)
|
||||||
|
await expect(roomsLocator.nth(idx).locator('strong + small')).toContainText(room.functionalName)
|
||||||
|
await expect(roomsLocator.nth(idx).locator('.badge')).toContainText(floor.short)
|
||||||
|
await roomsLocator.nth(idx).click()
|
||||||
|
await expect(roomsLocator.nth(idx)).toHaveClass(/active/)
|
||||||
|
await expect(page.locator('.floorplan .floorplan-rooms > .list-group-item:not(.active)')).toHaveCount(floor.rooms.length - 1)
|
||||||
|
// URL query segment
|
||||||
|
await expect(page.url()).toMatch(`room=${room.slug}`)
|
||||||
|
// Pin Drop
|
||||||
|
const planxRatio = (await floorImageLocator.evaluate(node => node.width)) / floor.width
|
||||||
|
const planyRatio = (await floorImageLocator.evaluate(node => node.height)) / floor.height
|
||||||
|
await expect(pinLocator).toBeVisible()
|
||||||
|
// eslint-disable-next-line no-useless-escape, quotes
|
||||||
|
const pinMarginLeft = await page.evaluate(`parseInt(window.getComputedStyle(document.querySelector('.floorplan .floorplan-plan-pin')).getPropertyValue('margin-left').match(/\\d+/))`)
|
||||||
|
const xPos = Math.round((room.left + (room.right - room.left) / 2) * planxRatio) - 25 + pinMarginLeft
|
||||||
|
const yPos = Math.round((room.top + (room.bottom - room.top) / 2) * planyRatio) - 40
|
||||||
|
const offsetLeft = await pinLocator.evaluate(node => node.offsetLeft)
|
||||||
|
const offsetTop = await pinLocator.evaluate(node => node.offsetTop)
|
||||||
|
expect(offsetLeft).toBe(xPos)
|
||||||
|
expect(offsetTop).toBe(yPos)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
65
yarn.lock
|
@ -2424,6 +2424,15 @@ browserlist@latest:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"builtins@npm:^5.0.1":
|
||||||
|
version: 5.0.1
|
||||||
|
resolution: "builtins@npm:5.0.1"
|
||||||
|
dependencies:
|
||||||
|
semver: ^7.0.0
|
||||||
|
checksum: 66d204657fe36522822a95b288943ad11b58f5eaede235b11d8c4edaa28ce4800087d44a2681524c340494aadb120a0068011acabe99d30e8f11a7d826d83515
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"c8@npm:7.12.0":
|
"c8@npm:7.12.0":
|
||||||
version: 7.12.0
|
version: 7.12.0
|
||||||
resolution: "c8@npm:7.12.0"
|
resolution: "c8@npm:7.12.0"
|
||||||
|
@ -3999,6 +4008,24 @@ browserlist@latest:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"eslint-plugin-n@npm:15.2.5":
|
||||||
|
version: 15.2.5
|
||||||
|
resolution: "eslint-plugin-n@npm:15.2.5"
|
||||||
|
dependencies:
|
||||||
|
builtins: ^5.0.1
|
||||||
|
eslint-plugin-es: ^4.1.0
|
||||||
|
eslint-utils: ^3.0.0
|
||||||
|
ignore: ^5.1.1
|
||||||
|
is-core-module: ^2.10.0
|
||||||
|
minimatch: ^3.1.2
|
||||||
|
resolve: ^1.22.1
|
||||||
|
semver: ^7.3.7
|
||||||
|
peerDependencies:
|
||||||
|
eslint: ">=7.0.0"
|
||||||
|
checksum: 3be265957b3dda6a049841803335c17689cf98a4b3859eeed3e57b44850b241e7d20640890b2dea7e83816c938fc16274bf78d370f571e211d00d9a3c513f281
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"eslint-plugin-node@npm:11.1.0":
|
"eslint-plugin-node@npm:11.1.0":
|
||||||
version: 11.1.0
|
version: 11.1.0
|
||||||
resolution: "eslint-plugin-node@npm:11.1.0"
|
resolution: "eslint-plugin-node@npm:11.1.0"
|
||||||
|
@ -5089,6 +5116,15 @@ browserlist@latest:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"is-core-module@npm:^2.10.0, is-core-module@npm:^2.9.0":
|
||||||
|
version: 2.10.0
|
||||||
|
resolution: "is-core-module@npm:2.10.0"
|
||||||
|
dependencies:
|
||||||
|
has: ^1.0.3
|
||||||
|
checksum: 0f3f77811f430af3256fa7bbc806f9639534b140f8ee69476f632c3e1eb4e28a38be0b9d1b8ecf596179c841b53576129279df95e7051d694dac4ceb6f967593
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"is-core-module@npm:^2.3.0, is-core-module@npm:^2.8.1":
|
"is-core-module@npm:^2.3.0, is-core-module@npm:^2.8.1":
|
||||||
version: 2.9.0
|
version: 2.9.0
|
||||||
resolution: "is-core-module@npm:2.9.0"
|
resolution: "is-core-module@npm:2.9.0"
|
||||||
|
@ -6947,6 +6983,19 @@ browserlist@latest:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"resolve@npm:^1.22.1":
|
||||||
|
version: 1.22.1
|
||||||
|
resolution: "resolve@npm:1.22.1"
|
||||||
|
dependencies:
|
||||||
|
is-core-module: ^2.9.0
|
||||||
|
path-parse: ^1.0.7
|
||||||
|
supports-preserve-symlinks-flag: ^1.0.0
|
||||||
|
bin:
|
||||||
|
resolve: bin/resolve
|
||||||
|
checksum: 07af5fc1e81aa1d866cbc9e9460fbb67318a10fa3c4deadc35c3ad8a898ee9a71a86a65e4755ac3195e0ea0cfbe201eb323ebe655ce90526fd61917313a34e4e
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"resolve@patch:resolve@^1.10.1#~builtin<compat/resolve>, resolve@patch:resolve@^1.15.1#~builtin<compat/resolve>, resolve@patch:resolve@^1.20.0#~builtin<compat/resolve>, resolve@patch:resolve@^1.22.0#~builtin<compat/resolve>":
|
"resolve@patch:resolve@^1.10.1#~builtin<compat/resolve>, resolve@patch:resolve@^1.15.1#~builtin<compat/resolve>, resolve@patch:resolve@^1.20.0#~builtin<compat/resolve>, resolve@patch:resolve@^1.22.0#~builtin<compat/resolve>":
|
||||||
version: 1.22.0
|
version: 1.22.0
|
||||||
resolution: "resolve@patch:resolve@npm%3A1.22.0#~builtin<compat/resolve>::version=1.22.0&hash=07638b"
|
resolution: "resolve@patch:resolve@npm%3A1.22.0#~builtin<compat/resolve>::version=1.22.0&hash=07638b"
|
||||||
|
@ -6960,6 +7009,19 @@ browserlist@latest:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"resolve@patch:resolve@^1.22.1#~builtin<compat/resolve>":
|
||||||
|
version: 1.22.1
|
||||||
|
resolution: "resolve@patch:resolve@npm%3A1.22.1#~builtin<compat/resolve>::version=1.22.1&hash=07638b"
|
||||||
|
dependencies:
|
||||||
|
is-core-module: ^2.9.0
|
||||||
|
path-parse: ^1.0.7
|
||||||
|
supports-preserve-symlinks-flag: ^1.0.0
|
||||||
|
bin:
|
||||||
|
resolve: bin/resolve
|
||||||
|
checksum: 5656f4d0bedcf8eb52685c1abdf8fbe73a1603bb1160a24d716e27a57f6cecbe2432ff9c89c2bd57542c3a7b9d14b1882b73bfe2e9d7849c9a4c0b8b39f02b8b
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"restore-cursor@npm:^3.1.0":
|
"restore-cursor@npm:^3.1.0":
|
||||||
version: 3.1.0
|
version: 3.1.0
|
||||||
resolution: "restore-cursor@npm:3.1.0"
|
resolution: "restore-cursor@npm:3.1.0"
|
||||||
|
@ -7055,6 +7117,7 @@ browserlist@latest:
|
||||||
eslint-config-standard: 17.0.0
|
eslint-config-standard: 17.0.0
|
||||||
eslint-plugin-cypress: 2.12.1
|
eslint-plugin-cypress: 2.12.1
|
||||||
eslint-plugin-import: 2.26.0
|
eslint-plugin-import: 2.26.0
|
||||||
|
eslint-plugin-n: 15.2.5
|
||||||
eslint-plugin-node: 11.1.0
|
eslint-plugin-node: 11.1.0
|
||||||
eslint-plugin-promise: 6.0.1
|
eslint-plugin-promise: 6.0.1
|
||||||
eslint-plugin-vue: 9.4.0
|
eslint-plugin-vue: 9.4.0
|
||||||
|
@ -7221,7 +7284,7 @@ browserlist@latest:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"semver@npm:^7.0.0, semver@npm:^7.3.2, semver@npm:^7.3.5, semver@npm:^7.3.6":
|
"semver@npm:^7.0.0, semver@npm:^7.3.2, semver@npm:^7.3.5, semver@npm:^7.3.6, semver@npm:^7.3.7":
|
||||||
version: 7.3.7
|
version: 7.3.7
|
||||||
resolution: "semver@npm:7.3.7"
|
resolution: "semver@npm:7.3.7"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|