Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

Add the new e2e tests that work with new_header flag on #1918

Merged
merged 6 commits into from
Oct 28, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/components/VCopyButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
type="button"
variant="secondary-filled"
size="disabled"
class="py-2 px-3 text-sr"
class="py-2 px-3 text-sr font-semibold"
:data-clipboard-target="el"
>
<span v-if="!success">
Expand Down
7 changes: 4 additions & 3 deletions src/components/VHeaderOld/VHeaderFilter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ import {
watch,
computed,
onMounted,
inject,
Ref,
toRef,
onBeforeUnmount,
} from '@nuxtjs/composition-api'
Expand All @@ -52,6 +50,7 @@ import { Portal as VTeleport } from 'portal-vue'
import { useBodyScrollLock } from '~/composables/use-body-scroll-lock'
import { useFilterSidebarVisibility } from '~/composables/use-filter-sidebar-visibility'
import { useFocusFilters } from '~/composables/use-focus-filters'
import { isMinScreen } from '~/composables/use-media-query'

import { Focus } from '~/utils/focus-management'
import { defineEvent } from '~/types/emits'
Expand Down Expand Up @@ -93,7 +92,9 @@ export default defineComponent({
const filterSidebar = useFilterSidebarVisibility()
const disabledRef = toRef(props, 'disabled')

const isMinScreenMd: Ref<boolean> = inject('isMinScreenMd')
// The `onMounted` in this component is run before the parent components' `onMounted` is run.
// The injected `isMinScreenMd` value can become true only after `default` layout's `onMounted` is run, so we need a separate check for `md` here to make sure that the value in `onMounted` is correct.
const isMinScreenMd = isMinScreen('md')

const open = () => (visibleRef.value = true)
const close = () => (visibleRef.value = false)
Expand Down
5 changes: 4 additions & 1 deletion src/layouts/default.vue
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,6 @@ const embeddedPage = {
mounted.value = true
})

const isMinScreenMd = isMinScreen('md')
/**
* If we use the `isMinScreen('lg')` composable for conditionally
* rendering components, we get a server-client side rendering
Expand All @@ -124,6 +123,10 @@ const embeddedPage = {
const isMinScreenLg = computed(() =>
Boolean(innerIsMinScreenLg.value && mounted.value)
)
const innerIsMinScreenMd = isMinScreen('md')
const isMinScreenMd = computed(() =>
Boolean(innerIsMinScreenMd.value && mounted.value)
)

const isSidebarVisible = computed(() => {
return isNewHeaderEnabled.value
Expand Down
11 changes: 4 additions & 7 deletions src/pages/search.vue
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,6 @@
import { isShallowEqualObjects } from '@wordpress/is-shallow-equal'
import { computed, defineComponent, inject, ref } from '@nuxtjs/composition-api'

import { Context } from '@nuxt/types'

import { isMinScreen } from '~/composables/use-media-query'
import { useFilterSidebarVisibility } from '~/composables/use-filter-sidebar-visibility'
import { Focus, focusIn } from '~/utils/focus-management'
import { useMediaStore } from '~/stores/media'
Expand All @@ -49,6 +46,8 @@ import VSearchGrid from '~/components/VSearchGrid.vue'
import VSkipToContentContainer from '~/components/VSkipToContentContainer.vue'
import VScrollButton from '~/components/VScrollButton.vue'

import type { Context } from '@nuxt/types'

export default defineComponent({
name: 'BrowsePage',
components: {
Expand All @@ -68,7 +67,6 @@ export default defineComponent({
scrollToTop: false,
setup() {
const searchGridRef = ref(null)
const isMinScreenMd = isMinScreen('md')
const { isVisible: isFilterSidebarVisible } = useFilterSidebarVisibility()
const showScrollButton = inject('showScrollButton')
const mediaStore = useMediaStore()
Expand Down Expand Up @@ -99,7 +97,6 @@ export default defineComponent({

return {
searchGridRef,
isMinScreenMd,
isFilterSidebarVisible,
showScrollButton,
searchTerm,
Expand Down Expand Up @@ -156,9 +153,9 @@ export default defineComponent({
}
focusIn(document.getElementById('__layout'), Focus.First)
},
fetchMedia(...args: unknown[]) {
fetchMedia({ shouldPersistMedia } = { shouldPersistMedia: false }) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is shouldPersistMedia always false here or should the type be boolean?

Suggested change
fetchMedia({ shouldPersistMedia } = { shouldPersistMedia: false }) {
fetchMedia({ shouldPersistMedia } = { shouldPersistMedia: boolean }) {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh dear, I totally misread this code! I thought it was the type but it was the default value 🤦 Sorry for the bad suggestion 😞

const mediaStore = useMediaStore(this.$pinia)
return mediaStore.fetchMedia(...args)
return mediaStore.fetchMedia({ shouldPersistMedia })
},
},
})
Expand Down
25 changes: 17 additions & 8 deletions src/pages/search/audio.vue
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,19 @@ import {
defineComponent,
useMeta,
ref,
inject,
watch,
toRef,
} from '@nuxtjs/composition-api'

import { isMinScreen } from '~/composables/use-media-query'
import { useBrowserIsMobile } from '~/composables/use-browser-detection'
import { useFocusFilters } from '~/composables/use-focus-filters'
import { Focus } from '~/utils/focus-management'

import { useUiStore } from '~/stores/ui'

import { IsMinScreenMdKey } from '~/types/provides'

import VSnackbar from '~/components/VSnackbar.vue'
import VAudioTrack from '~/components/VAudioTrack/VAudioTrack.vue'
import VLoadMore from '~/components/VLoadMore.vue'
Expand All @@ -69,16 +73,21 @@ export default defineComponent({

const results = computed(() => props.resultItems.audio)

const isMinScreenMd = isMinScreen('md', { shouldPassInSSR: true })
const isMinScreenMd = inject(IsMinScreenMdKey)
const filterVisibleRef = toRef(props, 'isFilterVisible')

// On SSR, we set the size to small if the User Agent is mobile, otherwise we set the size to medium.
const isMobile = useBrowserIsMobile()
const audioTrackSize = computed(() => {
return !isMinScreenMd.value || isMobile
? 's'
: props.isFilterVisible
? 'l'
: 'm'
const audioTrackSize = ref(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we'll need to revisit the audioTrackSize selection with @panchovm after the new_header is added. Currently, I feel like the l/m change on the md screen makes it look worse instead of better. And the code is fairly complicated.

!isMinScreenMd.value || isMobile ? 's' : props.isFilterVisible ? 'l' : 'm'
)

watch([filterVisibleRef, isMinScreenMd], ([filterVisible, isMd]) => {
if (!isMd) {
audioTrackSize.value = 's'
} else {
audioTrackSize.value = filterVisible ? 'l' : 'm'
}
})

const focusFilters = useFocusFilters()
Expand Down
217 changes: 217 additions & 0 deletions test/playwright/e2e/filters.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
import { test, expect, Page } from '@playwright/test'

import {
assertCheckboxStatus,
openFilters,
changeContentType,
goToSearchTerm,
enableNewHeader,
closeFilters,
isPageDesktop,
} from '~~/test/playwright/utils/navigation'

import { mockProviderApis } from '~~/test/playwright/utils/route'

import breakpoints from '~~/test/playwright/utils/breakpoints'

import {
supportedSearchTypes,
ALL_MEDIA,
IMAGE,
AUDIO,
} from '~/constants/media'

test.describe.configure({ mode: 'parallel' })

const assertCheckboxCount = async (
page: Page,
checked: 'checked' | 'notChecked' | 'total',
count: number
) => {
const checkedString = {
checked: ':checked',
notChecked: ':not(:checked)',
total: '',
}[checked]
const locatorString = `input[type="checkbox"]${checkedString}`
await expect(page.locator(locatorString)).toHaveCount(count, { timeout: 200 })
}

const FILTER_COUNTS = {
[ALL_MEDIA]: 11,
[AUDIO]: 32,
[IMAGE]: 70,
}

breakpoints.describeMobileAndDesktop(() => {
test.beforeEach(async ({ context, page }) => {
await mockProviderApis(context)
await enableNewHeader(page)
})
for (const searchType of supportedSearchTypes) {
test(`correct total number of filters is displayed for ${searchType}`, async ({
page,
}) => {
await goToSearchTerm(page, 'cat', { searchType })

await openFilters(page)

await assertCheckboxCount(page, 'total', FILTER_COUNTS[searchType])
})
}

test('initial filters are applied based on the url', async ({ page }) => {
await page.goto(
'/search/?q=cat&license_type=commercial&license=cc0&searchBy=creator'
)
await openFilters(page)
const expectedFilters = ['cc0', 'commercial', 'creator']

for (const checkbox of expectedFilters) {
await assertCheckboxStatus(page, checkbox)
}
})

test('common filters are retained when media type changes from all media to single type', async ({
page,
}) => {
await page.goto(
'/search/?q=cat&license_type=commercial&license=cc0&searchBy=creator'
)
await openFilters(page)
const expectedFilters = ['cc0', 'commercial', 'creator']

for (const checkbox of expectedFilters) {
await assertCheckboxStatus(page, checkbox)
}
await changeContentType(page, 'Images')

await expect(page).toHaveURL(
'/search/image?q=cat&license_type=commercial&license=cc0&searchBy=creator'
)
await openFilters(page)
for (const checkbox of expectedFilters) {
await assertCheckboxStatus(page, checkbox)
}
})

test('common filters are retained when media type changes from single type to all media', async ({
page,
}) => {
await page.goto(
'/search/image?q=cat&license_type=commercial&license=cc0&searchBy=creator'
)
await openFilters(page)

for (const checkbox of ['cc0', 'commercial', 'creator']) {
await assertCheckboxStatus(page, checkbox)
}

await changeContentType(page, 'All content')

await openFilters(page)
await expect(page.locator('input[type="checkbox"]:checked')).toHaveCount(3)

await expect(page).toHaveURL(
'/search/?q=cat&license_type=commercial&license=cc0&searchBy=creator'
)
})

test('selecting some filters can disable dependent filters', async ({
page,
}) => {
await page.goto('/search/audio?q=cat&license_type=commercial')
await openFilters(page)

// by-nc is special because we normally test for fuzzy match, and by-nc matches 3 labels.
const byNc = page.locator('input[value="by-nc"]')
await expect(byNc).toBeDisabled()
for (const checkbox of ['by-nc-sa', 'by-nc-nd']) {
await assertCheckboxStatus(page, checkbox, '', 'disabled')
}
await assertCheckboxStatus(page, 'commercial')

await page.click('label:has-text("commercial")')

await assertCheckboxStatus(page, 'commercial', '', 'unchecked')
await expect(byNc).not.toBeDisabled()
for (const checkbox of ['commercial', 'by-nc-sa', 'by-nc-nd']) {
await assertCheckboxStatus(page, checkbox, '', 'unchecked')
}
})

/**
* When the search type changes:
* - image-specific filter (aspect_ration=tall) is discarded
* - common filter (license_type=CC0) is kept
* - filter button text updates
* - URL updates
* Tests for the missing checkbox with `toHaveCount` are flaky, so we use filter button
* text and the URL instead.
*/
test('filters are updated when media type changes', async ({ page }) => {
await page.goto('/search/image?q=cat&aspect_ratio=tall&license=cc0')
await openFilters(page)

await assertCheckboxStatus(page, 'tall')
await assertCheckboxStatus(page, 'cc0')

await changeContentType(page, 'Audio')
await openFilters(page)

// Only CC0 checkbox is checked, and the filter button label is
// '1 Filter' on `xl` or '1' on `lg` screens
await assertCheckboxStatus(page, 'cc0')
await closeFilters(page)
if (isPageDesktop(page)) {
const filterButtonText = await page
.locator('[aria-controls="filters"] span:visible')
.textContent()
expect(filterButtonText).toContain('1')
} else {
const filtersAriaLabel =
(await page
.locator('[aria-controls="content-settings-modal"]')
.getAttribute('aria-label')) ?? ''
expect(filtersAriaLabel.trim()).toEqual('Menu. 1 filter applied')
}

await expect(page).toHaveURL('/search/audio?q=cat&license=cc0')
})

test('new media request is sent when a filter is selected', async ({
page,
}) => {
await page.goto('/search/image?q=cat')
await openFilters(page)

await assertCheckboxStatus(page, 'cc0', '', 'unchecked')

const [response] = await Promise.all([
page.waitForResponse((response) => response.url().includes('cc0')),
page.click('label:has-text("CC0")'),
])

await assertCheckboxStatus(page, 'cc0')
// Remove the host url and path because when proxied, the 'http://localhost:49153' is used instead of the
// real API url
const queryString = response.url().split('/images/')[1]
expect(queryString).toEqual('?q=cat&license=cc0')
})

for (const [searchType, source] of [
['audio', 'Freesound'],
['image', 'Flickr'],
]) {
test(`Provider filters are correctly initialized from the URL: ${source} - ${searchType}`, async ({
page,
}) => {
await page.goto(
`/search/${searchType}?q=birds&source=${source.toLowerCase()}`
)
await openFilters(page)

await assertCheckboxStatus(page, source, '', 'checked')
})
}
})
Loading