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
|
||||
path: coverage.json
|
||||
|
||||
tests-cypress-modern:
|
||||
name: Run Tests (Cypress - Modern)
|
||||
tests-playwright:
|
||||
name: Run Tests (Playwright)
|
||||
if: ${{ github.event.inputs.skiptests == 'false' }}
|
||||
needs: [prepare]
|
||||
runs-on: macos-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
project: [chromium, firefox]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '18'
|
||||
|
||||
- name: Run all tests
|
||||
run: |
|
||||
echo "Installing dependencies..."
|
||||
yarn
|
||||
echo "Start Vite Preview..."
|
||||
yarn preview &>/dev/null &
|
||||
echo "Installing Playwright..."
|
||||
cd playwright
|
||||
mkdir test-results
|
||||
npm ci
|
||||
npx playwright install --with-deps ${{ matrix.project }}
|
||||
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
|
||||
if: ${{ always() }}
|
||||
continue-on-error: true
|
||||
with:
|
||||
name: videos-modern
|
||||
path: cypress/videos/
|
||||
name: playwright-results-${{ matrix.project }}
|
||||
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:
|
||||
name: Run Tests (Cypress - Legacy)
|
||||
tests-cypress:
|
||||
name: Run Tests (Cypress)
|
||||
if: ${{ github.event.inputs.skiptests == 'false' }}
|
||||
needs: [prepare]
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -256,6 +260,7 @@ jobs:
|
|||
with:
|
||||
name: videos-legacy
|
||||
path: cypress/videos/
|
||||
if-no-files-found: ignore
|
||||
|
||||
- name: Upload Screenshots
|
||||
uses: actions/upload-artifact@v3.0.0
|
||||
|
@ -264,6 +269,7 @@ jobs:
|
|||
with:
|
||||
name: screenshots-modern
|
||||
path: cypress/screenshots/
|
||||
if-no-files-found: ignore
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# RELEASE
|
||||
|
@ -271,7 +277,7 @@ jobs:
|
|||
release:
|
||||
name: Make Release
|
||||
if: ${{ always() }}
|
||||
needs: [tests-python, tests-cypress-modern, tests-cypress-legacy, prepare]
|
||||
needs: [tests-python, tests-playwright, tests-cypress, prepare]
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
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-plugin-cypress", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:2.12.1"],\
|
||||
["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-promise", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.0.1"],\
|
||||
["eslint-plugin-vue", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:9.4.0"],\
|
||||
|
@ -3095,10 +3096,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
|||
}]\
|
||||
]],\
|
||||
["builtins", [\
|
||||
["npm:4.1.0", {\
|
||||
"packageLocation": "./.yarn/cache/builtins-npm-4.1.0-b8969ccdfe-3524f5a589.zip/node_modules/builtins/",\
|
||||
["npm:5.0.1", {\
|
||||
"packageLocation": "./.yarn/cache/builtins-npm-5.0.1-6d4820dd76-66d204657f.zip/node_modules/builtins/",\
|
||||
"packageDependencies": [\
|
||||
["builtins", "npm:4.1.0"],\
|
||||
["builtins", "npm:5.0.1"],\
|
||||
["semver", "npm:7.3.7"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
|
@ -4862,7 +4863,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
|||
["@types/eslint-plugin-promise", null],\
|
||||
["eslint", "npm:8.23.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"]\
|
||||
],\
|
||||
"packagePeers": [\
|
||||
|
@ -4871,6 +4872,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
|||
"@types/eslint-plugin-promise",\
|
||||
"@types/eslint",\
|
||||
"eslint-plugin-import",\
|
||||
"eslint-plugin-n",\
|
||||
"eslint-plugin-promise",\
|
||||
"eslint"\
|
||||
],\
|
||||
|
@ -4962,10 +4964,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
|||
],\
|
||||
"linkType": "SOFT"\
|
||||
}],\
|
||||
["virtual:5cccaf00e87dfff96dbbb5eaf7a3055373358b8114d6a1adfb32f54ed6b40ba06068d3aa1fdd8062899a0cad040f68c17cc6b72bac2cdbe9700f3d6330d112f3#npm:3.0.1", {\
|
||||
"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/",\
|
||||
["virtual:01ca464921c9b9f6f05ff324a93e38459f6f1b69ef551b0ff88f975ab15594aa2d383b8c5900d3e9ebd3f172ab1a572bfe06ea18bc9ed6aab7ddec552080fdee#npm:4.1.0", {\
|
||||
"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": [\
|
||||
["eslint-plugin-es", "virtual:5cccaf00e87dfff96dbbb5eaf7a3055373358b8114d6a1adfb32f54ed6b40ba06068d3aa1fdd8062899a0cad040f68c17cc6b72bac2cdbe9700f3d6330d112f3#npm:3.0.1"],\
|
||||
["eslint-plugin-es", "virtual:01ca464921c9b9f6f05ff324a93e38459f6f1b69ef551b0ff88f975ab15594aa2d383b8c5900d3e9ebd3f172ab1a572bfe06ea18bc9ed6aab7ddec552080fdee#npm:4.1.0"],\
|
||||
["@types/eslint", null],\
|
||||
["eslint", "npm:8.23.0"],\
|
||||
["eslint-utils", "npm:2.1.0"],\
|
||||
|
@ -4977,10 +4979,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
|||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["virtual:c8e07c2a6b56869a5a6a64a5a2bbfafd2756fdfec2e39799aafeb55edf967e3394125f03e2dbe23e573456f9dc23ed0ee125b7a467ac99d6b6dc124c1fb861dd#npm:4.1.0", {\
|
||||
"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/",\
|
||||
["virtual:5cccaf00e87dfff96dbbb5eaf7a3055373358b8114d6a1adfb32f54ed6b40ba06068d3aa1fdd8062899a0cad040f68c17cc6b72bac2cdbe9700f3d6330d112f3#npm:3.0.1", {\
|
||||
"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": [\
|
||||
["eslint-plugin-es", "virtual:c8e07c2a6b56869a5a6a64a5a2bbfafd2756fdfec2e39799aafeb55edf967e3394125f03e2dbe23e573456f9dc23ed0ee125b7a467ac99d6b6dc124c1fb861dd#npm:4.1.0"],\
|
||||
["eslint-plugin-es", "virtual:5cccaf00e87dfff96dbbb5eaf7a3055373358b8114d6a1adfb32f54ed6b40ba06068d3aa1fdd8062899a0cad040f68c17cc6b72bac2cdbe9700f3d6330d112f3#npm:3.0.1"],\
|
||||
["@types/eslint", null],\
|
||||
["eslint", "npm:8.23.0"],\
|
||||
["eslint-utils", "npm:2.1.0"],\
|
||||
|
@ -5033,27 +5035,27 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
|||
}]\
|
||||
]],\
|
||||
["eslint-plugin-n", [\
|
||||
["npm:15.2.0", {\
|
||||
"packageLocation": "./.yarn/cache/eslint-plugin-n-npm-15.2.0-669e8b723e-4303dea35a.zip/node_modules/eslint-plugin-n/",\
|
||||
["npm:15.2.5", {\
|
||||
"packageLocation": "./.yarn/cache/eslint-plugin-n-npm-15.2.5-1f66a5b3be-3be265957b.zip/node_modules/eslint-plugin-n/",\
|
||||
"packageDependencies": [\
|
||||
["eslint-plugin-n", "npm:15.2.0"]\
|
||||
["eslint-plugin-n", "npm:15.2.5"]\
|
||||
],\
|
||||
"linkType": "SOFT"\
|
||||
}],\
|
||||
["virtual:5de208ba69f1abc06b9e76e02d4ce4fc49fcab4f9a21e07e70644bde43652129abc0a8d2e76e362701d48e3019da8bcf1b9d697031ba71659718edb10a775408#npm:15.2.0", {\
|
||||
"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/",\
|
||||
["virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:15.2.5", {\
|
||||
"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": [\
|
||||
["eslint-plugin-n", "virtual:5de208ba69f1abc06b9e76e02d4ce4fc49fcab4f9a21e07e70644bde43652129abc0a8d2e76e362701d48e3019da8bcf1b9d697031ba71659718edb10a775408#npm:15.2.0"],\
|
||||
["eslint-plugin-n", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:15.2.5"],\
|
||||
["@types/eslint", null],\
|
||||
["builtins", "npm:4.1.0"],\
|
||||
["builtins", "npm:5.0.1"],\
|
||||
["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"],\
|
||||
["ignore", "npm:5.2.0"],\
|
||||
["is-core-module", "npm:2.9.0"],\
|
||||
["is-core-module", "npm:2.10.0"],\
|
||||
["minimatch", "npm:3.1.2"],\
|
||||
["resolve", "patch:resolve@npm%3A1.22.0#~builtin<compat/resolve>::version=1.22.0&hash=07638b"],\
|
||||
["semver", "npm:6.3.0"]\
|
||||
["resolve", "patch:resolve@npm%3A1.22.1#~builtin<compat/resolve>::version=1.22.1&hash=07638b"],\
|
||||
["semver", "npm:7.3.7"]\
|
||||
],\
|
||||
"packagePeers": [\
|
||||
"@types/eslint",\
|
||||
|
@ -6291,6 +6293,14 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
|||
}]\
|
||||
]],\
|
||||
["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", {\
|
||||
"packageLocation": "./.yarn/cache/is-core-module-npm-2.9.0-5ba77c35ae-b27034318b.zip/node_modules/is-core-module/",\
|
||||
"packageDependencies": [\
|
||||
|
@ -8417,6 +8427,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
|||
["supports-preserve-symlinks-flag", "npm:1.0.0"]\
|
||||
],\
|
||||
"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", [\
|
||||
|
@ -8528,6 +8548,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
|||
["eslint-config-standard", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:17.0.0"],\
|
||||
["eslint-plugin-cypress", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:2.12.1"],\
|
||||
["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-promise", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.0.1"],\
|
||||
["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)
|
||||
|
||||
// INIT
|
||||
|
||||
agendaStore.fetch()
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// Handle browser resize
|
||||
// --------------------------------------------------------------------
|
||||
|
|
|
@ -227,8 +227,8 @@ const titleExtra = computed(() => {
|
|||
return title
|
||||
})
|
||||
const meetingDate = computed(() => {
|
||||
const start = DateTime.fromISO(agendaStore.meeting.startDate).setZone(agendaStore.timezone)
|
||||
const end = DateTime.fromISO(agendaStore.meeting.endDate).setZone(agendaStore.timezone)
|
||||
const start = DateTime.fromISO(agendaStore.meeting.startDate, { zone: agendaStore.meeting.timezone }).setZone(agendaStore.timezone)
|
||||
const end = DateTime.fromISO(agendaStore.meeting.endDate, { zone: agendaStore.meeting.timezone }).setZone(agendaStore.timezone)
|
||||
if (start.month === end.month) {
|
||||
return `${start.toFormat('MMMM d')} - ${end.toFormat('d, y')}`
|
||||
} else {
|
||||
|
@ -353,6 +353,8 @@ onBeforeUnmount(() => {
|
|||
// MOUNTED
|
||||
|
||||
onMounted(() => {
|
||||
agendaStore.fetch(route.params.meetingNumber)
|
||||
|
||||
handleCurrentMeetingRedirect()
|
||||
|
||||
// -> Hide Loading Screen
|
||||
|
|
|
@ -31,7 +31,7 @@ n-drawer(v-model:show='isShown', placement='right', :width='panelWidth')
|
|||
n-divider(title-placement='left')
|
||||
i.bi.bi-globe.me-2
|
||||
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(
|
||||
style='flex-grow: 1;'
|
||||
: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`'
|
||||
@click='setTimezone(`UTC`)'
|
||||
) UTC
|
||||
n-select.mt-2(
|
||||
n-select.mt-2#agenda-settings-tz-ddn(
|
||||
v-model:value='agendaStore.timezone'
|
||||
:options='timezones'
|
||||
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
|
||||
//- n-switch.me-3(v-model:value='agendaStore.listDayCollapse', disabled)
|
||||
//- 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(
|
||||
v-model:value='agendaStore.colorLegendShown'
|
||||
aria-label='Display Color Legend'
|
||||
|
@ -71,7 +71,7 @@ n-drawer(v-model:show='isShown', placement='right', :width='panelWidth')
|
|||
template(#trigger)
|
||||
i.bi.bi-info-circle
|
||||
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(
|
||||
v-model:value='agendaStore.infoNoteShown'
|
||||
aria-label='Display Current Meeting Info Note'
|
||||
|
@ -81,19 +81,19 @@ n-drawer(v-model:show='isShown', placement='right', :width='panelWidth')
|
|||
template(#trigger)
|
||||
i.bi.bi-info-circle
|
||||
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(
|
||||
v-model:value='agendaStore.eventIconsShown'
|
||||
aria-label='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(
|
||||
v-model:value='agendaStore.floorIndicatorsShown'
|
||||
aria-label='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(
|
||||
v-model:value='agendaStore.areaIndicatorsShown'
|
||||
aria-label='Display Group Area Indicators'
|
||||
|
@ -103,7 +103,7 @@ n-drawer(v-model:show='isShown', placement='right', :width='panelWidth')
|
|||
template(#trigger)
|
||||
i.bi.bi-info-circle
|
||||
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(
|
||||
v-model:value='agendaStore.redhandShown'
|
||||
aria-label='Display Realtime Red Line'
|
||||
|
@ -113,7 +113,7 @@ n-drawer(v-model:show='isShown', placement='right', :width='panelWidth')
|
|||
template(#trigger)
|
||||
i.bi.bi-info-circle
|
||||
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(
|
||||
v-model:value='agendaStore.bolderText'
|
||||
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')
|
||||
i.bi.bi-palette.me-2
|
||||
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(
|
||||
:modes='[`hex`]'
|
||||
:render-label='() => {}'
|
||||
|
|
|
@ -216,10 +216,9 @@ onBeforeUnmount(() => {
|
|||
// MOUNTED
|
||||
|
||||
onMounted(() => {
|
||||
// -> Go to current meeting if not provided
|
||||
if (!route.params.meetingNumber && agendaStore.meeting.number) {
|
||||
router.replace({ params: { meetingNumber: agendaStore.meeting.number } })
|
||||
}
|
||||
agendaStore.fetch(route.params.meetingNumber)
|
||||
|
||||
handleCurrentMeetingRedirect()
|
||||
|
||||
// -> Hide Loading Screen
|
||||
if (agendaStore.isLoaded) {
|
||||
|
|
|
@ -133,11 +133,14 @@ export const useAgendaStore = defineStore('agenda', {
|
|||
}
|
||||
},
|
||||
actions: {
|
||||
async fetch () {
|
||||
async fetch (meetingNumber) {
|
||||
try {
|
||||
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) {
|
||||
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',
|
||||
configurePreviewServer(server) {
|
||||
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-plugin-cypress": "2.12.1",
|
||||
"eslint-plugin-import": "2.26.0",
|
||||
"eslint-plugin-n": "15.2.5",
|
||||
"eslint-plugin-node": "11.1.0",
|
||||
"eslint-plugin-promise": "6.0.1",
|
||||
"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'
|
||||
import { faker } from '@faker-js/faker'
|
||||
import seedrandom from 'seedrandom'
|
||||
import _lodash from 'lodash' // Cannot use lodash-es as we need to runInContext for constant randomness
|
||||
import { startCase, times } from 'lodash-es'
|
||||
import slugify from 'slugify'
|
||||
import ms from 'ms'
|
||||
const { DateTime } = require('luxon')
|
||||
const { faker } = require('@faker-js/faker')
|
||||
const seedrandom = require('seedrandom')
|
||||
const _ = require('lodash')
|
||||
const slugify = require('slugify')
|
||||
const ms = require('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 })
|
||||
|
||||
|
@ -20,7 +22,7 @@ const sessionsWithWebex = [3, 4]
|
|||
// Use constant randomness seed
|
||||
seedrandom(TEST_SEED.toString(), { global: true })
|
||||
faker.seed(TEST_SEED)
|
||||
const { sample, sampleSize } = _lodash.runInContext()
|
||||
const { sample, sampleSize } = _.runInContext()
|
||||
|
||||
/**
|
||||
* Generate area response from label + children
|
||||
|
@ -85,7 +87,6 @@ function findAreaGroup (slug, areas) {
|
|||
throw new Error('Requested group does not exist!')
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reverse areas and groups mapping
|
||||
*/
|
||||
|
@ -142,10 +143,12 @@ function createEvent ({
|
|||
id: ++lastEventId,
|
||||
sessionId: ++lastSessionId,
|
||||
room: room.name,
|
||||
location: hasLocation ? {
|
||||
location: hasLocation
|
||||
? {
|
||||
short: floor.short,
|
||||
name: floor.name
|
||||
} : {},
|
||||
}
|
||||
: {},
|
||||
acronym: group.keyword,
|
||||
duration: typeof duration === 'string' ? ms(duration) / 1000 : duration,
|
||||
name: eventName,
|
||||
|
@ -154,9 +157,9 @@ function createEvent ({
|
|||
type,
|
||||
isBoF,
|
||||
filterKeywords: [
|
||||
"coding",
|
||||
"hackathon",
|
||||
"hackathon-sessc"
|
||||
'coding',
|
||||
'hackathon',
|
||||
'hackathon-sessc'
|
||||
],
|
||||
groupAcronym: group.keyword,
|
||||
groupName: faker.lorem.sentence(faker.mersenne.rand(5, 2)),
|
||||
|
@ -170,22 +173,24 @@ function createEvent ({
|
|||
showAgenda
|
||||
},
|
||||
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,
|
||||
short: eventName,
|
||||
sessionToken: "sessa",
|
||||
sessionToken: 'sessa',
|
||||
links: {
|
||||
chat: `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`,
|
||||
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"
|
||||
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,
|
||||
audioStream: hasAgenda ? 'https://mp3.conf.meetecho.com/ietf123/{group.acronym}/{order_number}.m3u' : null,
|
||||
webex: hasWebex ? 'https://webex.com/123' : null,
|
||||
|
@ -195,7 +200,7 @@ function createEvent ({
|
|||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
module.exports = {
|
||||
/**
|
||||
* Generate a standard agenda data reponse
|
||||
*/
|
||||
|
@ -219,17 +224,17 @@ export default {
|
|||
const endDate = startDate.plus({ days: 7 })
|
||||
|
||||
// Generate floors
|
||||
const floors = times(6, (idx) => {
|
||||
const floors = _.times(6, (idx) => {
|
||||
const floorIdx = idx + 1
|
||||
const floor = floorsMeta[idx]
|
||||
return {
|
||||
id: floorIdx,
|
||||
image: `/media/floor/${floor.path}`,
|
||||
name: `Level ${startCase(faker.color.human())} ${floorIdx}`,
|
||||
name: `Level ${_.startCase(faker.color.human())} ${floorIdx}`,
|
||||
short: `L${floorIdx}`,
|
||||
width: floor.width,
|
||||
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}`
|
||||
// Keep 10% margin on each side
|
||||
const roomXUnit = Math.round(floor.width / 10)
|
||||
|
@ -239,7 +244,7 @@ export default {
|
|||
return {
|
||||
id: floorIdx * 100 + ridx,
|
||||
name: roomName,
|
||||
functionalName: startCase(faker.lorem.words(2)),
|
||||
functionalName: _.startCase(faker.lorem.words(2)),
|
||||
slug: xslugify(roomName),
|
||||
left: roomX,
|
||||
right: roomX + roomXUnit,
|
||||
|
@ -262,7 +267,7 @@ export default {
|
|||
for (const area of firstAreasNames) {
|
||||
firstAreas.push(createArea({
|
||||
label: area,
|
||||
children: times(faker.mersenne.rand(25, 2), (idx) => {
|
||||
children: _.times(faker.mersenne.rand(25, 2), (idx) => {
|
||||
return createGroup({ mayBeBof: true })
|
||||
})
|
||||
}))
|
||||
|
@ -275,7 +280,7 @@ export default {
|
|||
for (const area of ['UVW', 'XYZ0']) {
|
||||
secondAreas.push(createArea({
|
||||
label: area,
|
||||
children: times(faker.mersenne.rand(25, 2), (idx) => {
|
||||
children: _.times(faker.mersenne.rand(25, 2), (idx) => {
|
||||
return createGroup({ mayBeBof: true })
|
||||
})
|
||||
}))
|
||||
|
@ -301,13 +306,13 @@ export default {
|
|||
}),
|
||||
createArea({
|
||||
label: 'Office hours',
|
||||
children: firstAreasNames.map(n => createGroup({ label: `${n} Office Hours`}))
|
||||
children: firstAreasNames.map(n => createGroup({ label: `${n} Office Hours` }))
|
||||
}),
|
||||
createArea({
|
||||
label: 'Open meeting',
|
||||
children: [
|
||||
createGroup({ label: 'WG Chairs Forum' }),
|
||||
createGroup({ label: `Newcomers' Feedback Session` })
|
||||
createGroup({ label: 'Newcomers\' Feedback Session' })
|
||||
]
|
||||
}),
|
||||
createArea({
|
||||
|
@ -327,7 +332,7 @@ export default {
|
|||
createArea({
|
||||
label: 'Social',
|
||||
children: [
|
||||
createGroup({ label: `Newcomers' Quick Connections` }),
|
||||
createGroup({ label: 'Newcomers\' Quick Connections' }),
|
||||
createGroup({ label: 'Welcome Reception', toggledBy: ['ietf'] }),
|
||||
createGroup({ label: 'Break', toggledBy: ['secretariat'] }),
|
||||
createGroup({ label: 'Beverage and Snack Break', toggledBy: ['secretariat'] }),
|
||||
|
@ -337,7 +342,7 @@ export default {
|
|||
createArea({
|
||||
label: 'Tutorial',
|
||||
children: [
|
||||
createGroup({ label: `Tutorial: Newcomers' Overview` })
|
||||
createGroup({ label: 'Tutorial: Newcomers\' Overview' })
|
||||
]
|
||||
}),
|
||||
createArea({
|
||||
|
@ -434,7 +439,7 @@ export default {
|
|||
}, floors))
|
||||
|
||||
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 }),
|
||||
duration: '1h',
|
||||
...findAreaGroup('newcomers-quick-connections', categories[2]),
|
||||
|
@ -506,7 +511,7 @@ export default {
|
|||
}, floors))
|
||||
|
||||
// -> Session I
|
||||
times(8, () => { // 8 lanes per session time
|
||||
_.times(8, () => { // 8 lanes per session time
|
||||
const { area, ...group } = daySessions.pop()
|
||||
schedule.push(createEvent({
|
||||
name: 'Session I',
|
||||
|
@ -535,7 +540,7 @@ export default {
|
|||
}, floors))
|
||||
|
||||
// -> Session II
|
||||
times(8, () => { // 8 lanes per session time
|
||||
_.times(8, () => { // 8 lanes per session time
|
||||
const { area, ...group } = daySessions.pop()
|
||||
schedule.push(createEvent({
|
||||
name: 'Session II',
|
||||
|
@ -566,7 +571,7 @@ export default {
|
|||
}, floors))
|
||||
|
||||
// -> Session III
|
||||
times(8, () => { // 8 lanes per session time
|
||||
_.times(8, () => { // 8 lanes per session time
|
||||
const { area, ...group } = daySessions.pop()
|
||||
schedule.push(createEvent({
|
||||
name: 'Session III',
|
||||
|
@ -627,5 +632,41 @@ export default {
|
|||
schedule,
|
||||
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
|
||||
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":
|
||||
version: 7.12.0
|
||||
resolution: "c8@npm:7.12.0"
|
||||
|
@ -3999,6 +4008,24 @@ browserlist@latest:
|
|||
languageName: node
|
||||
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":
|
||||
version: 11.1.0
|
||||
resolution: "eslint-plugin-node@npm:11.1.0"
|
||||
|
@ -5089,6 +5116,15 @@ browserlist@latest:
|
|||
languageName: node
|
||||
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":
|
||||
version: 2.9.0
|
||||
resolution: "is-core-module@npm:2.9.0"
|
||||
|
@ -6947,6 +6983,19 @@ browserlist@latest:
|
|||
languageName: node
|
||||
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>":
|
||||
version: 1.22.0
|
||||
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
|
||||
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":
|
||||
version: 3.1.0
|
||||
resolution: "restore-cursor@npm:3.1.0"
|
||||
|
@ -7055,6 +7117,7 @@ browserlist@latest:
|
|||
eslint-config-standard: 17.0.0
|
||||
eslint-plugin-cypress: 2.12.1
|
||||
eslint-plugin-import: 2.26.0
|
||||
eslint-plugin-n: 15.2.5
|
||||
eslint-plugin-node: 11.1.0
|
||||
eslint-plugin-promise: 6.0.1
|
||||
eslint-plugin-vue: 9.4.0
|
||||
|
@ -7221,7 +7284,7 @@ browserlist@latest:
|
|||
languageName: node
|
||||
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
|
||||
resolution: "semver@npm:7.3.7"
|
||||
dependencies:
|
||||
|
|