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
This commit is contained in:
Nicolas Giard 2022-09-20 14:33:22 -04:00 committed by GitHub
parent 5c8545eecb
commit 143877ec3d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
39 changed files with 6065 additions and 23508 deletions

View file

@ -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
View file

@ -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"],\

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -24,10 +24,6 @@ const agendaStore = useAgendaStore()
const appContainer = ref(null)
// INIT
agendaStore.fetch()
// --------------------------------------------------------------------
// Handle browser resize
// --------------------------------------------------------------------

View file

@ -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

View file

@ -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='() => {}'

View file

@ -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) {

View file

@ -133,11 +133,14 @@ export const useAgendaStore = defineStore('agenda', {
}
},
actions: {
async fetch () {
async fetch (meetingNumber) {
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) {
throw new Error(resp.statusText)
}

View file

@ -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)
})
})
})
})

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -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)
})
}
}

View file

@ -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
View 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
View 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
View file

@ -0,0 +1,4 @@
node_modules/
/test-results/
/playwright-report/
/playwright/.cache/

57
playwright/README.md Normal file
View 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
```

View file

Before

Width:  |  Height:  |  Size: 320 KiB

After

Width:  |  Height:  |  Size: 320 KiB

View file

Before

Width:  |  Height:  |  Size: 198 KiB

After

Width:  |  Height:  |  Size: 198 KiB

View file

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 52 KiB

View file

Before

Width:  |  Height:  |  Size: 119 KiB

After

Width:  |  Height:  |  Size: 119 KiB

View file

Before

Width:  |  Height:  |  Size: 139 KiB

After

Width:  |  Height:  |  Size: 139 KiB

View file

Before

Width:  |  Height:  |  Size: 492 KiB

After

Width:  |  Height:  |  Size: 492 KiB

View 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
})
}
}

View file

@ -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 ? {
short: floor.short,
name: floor.name
} : {},
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 ? [
{
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"
}
] : [],
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'
}
]
: [],
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
}
}

View 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

File diff suppressed because it is too large Load diff

32
playwright/package.json Normal file
View 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"
}
}

View 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

File diff suppressed because it is too large Load diff

View 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)
}
})
})
}
})

View file

@ -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: