Skip to content

Commit

Permalink
test coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
Shane Osbourne committed Oct 21, 2024
1 parent 4a6ece4 commit ed057dc
Show file tree
Hide file tree
Showing 12 changed files with 366 additions and 213 deletions.
53 changes: 30 additions & 23 deletions special-pages/pages/new-tab/app/favorites/Favorites.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,16 +68,19 @@ export function FavoritesConfigured ({ gridRef, favorites, listDidReOrder, expan

// only recompute the list
const items = useMemo(() => {
return favorites.map((item) => (
<TileMemo
data={item.data}
favicon={item.favicon}
title={item.title}
key={item.id}
prefix={ITEM_PREFIX}
id={item.id}
/>
)).concat(Array.from({ length: placeholders }).map((_, index) => {
return favorites.map((item, index) => {
return (
<TileMemo
data={item.data}
favicon={item.favicon}
title={item.title}
key={item.id}
id={item.id}
index={index}
visible={true}
/>
)
}).concat(Array.from({ length: placeholders }).map((_, index) => {
if (index === 0) {
return <PlusIconMemo key="placeholder-plus" onClick={add} />
}
Expand All @@ -103,6 +106,8 @@ export function FavoritesConfigured ({ gridRef, favorites, listDidReOrder, expan
}
}

const canToggleExpansion = items.length > ROW_CAPACITY

return (
<div class={styles.root} data-testid="FavoritesConfigured">
<div
Expand All @@ -116,21 +121,23 @@ export function FavoritesConfigured ({ gridRef, favorites, listDidReOrder, expan
<div
className={cn({
[styles.showhide]: true,
[styles.showhideVisible]: items.length > ROW_CAPACITY
[styles.showhideVisible]: canToggleExpansion
})}
>
<ShowHideButton
buttonAttrs={{
'aria-expanded': expansion === 'expanded',
'aria-pressed': expansion === 'expanded',
'aria-controls': WIDGET_ID,
id: TOGGLE_ID
}}
text={expansion === 'expanded'
? t('favorites_show_less')
: t('favorites_show_more', { count: String(hiddenCount) })}
onClick={toggle}
/>
{canToggleExpansion && (
<ShowHideButton
buttonAttrs={{
'aria-expanded': expansion === 'expanded',
'aria-pressed': expansion === 'expanded',
'aria-controls': WIDGET_ID,
id: TOGGLE_ID
}}
text={expansion === 'expanded'
? t('favorites_show_less')
: t('favorites_show_more', { count: String(hiddenCount) })}
onClick={toggle}
/>
)}
</div>
</div>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,20 +74,20 @@
}

.draggable {
background: var(--color-black-at-3);
background-color: var(--color-black-at-3);

&:hover {
background: var(--color-black-at-9);
background-color: var(--color-black-at-9);
}

&:active {
transform: scale(0.95);
}

@media screen and (prefers-color-scheme: dark) {
background: var(--color-white-at-9);
background-color: var(--color-white-at-9);
&:hover {
background: var(--color-white-at-12);
background-color: var(--color-white-at-12);
}
}
}
Expand Down
6 changes: 5 additions & 1 deletion special-pages/pages/new-tab/app/favorites/FavouritesGrid.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,11 @@ export function useGridState (favorites, setFavorites, animation) {
})

flushSync(() => {
setFavorites(reorderedList, startId, targetIndex)
try {
setFavorites(reorderedList, startId, targetIndex)
} catch (e) {
console.error('did catch', e)
}
})

const htmlElem = source.element
Expand Down
11 changes: 5 additions & 6 deletions special-pages/pages/new-tab/app/favorites/Tile.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ import { InstanceIdContext } from './FavouritesGrid.js'
* @param {Favorite['id']} props.id
* @param {Favorite['title']} props.title
* @param {Favorite['favicon']} props.favicon
* @param {string} props.prefix - unique id for the parent
* @param {number} props.index
* @param {boolean} props.visible
*/
export function Tile ({ data, favicon, title, id, prefix }) {
export function Tile ({ data, visible, favicon, index, title, id }) {
const { state, ref } = useTileState(data, id)

return (
Expand All @@ -37,15 +38,13 @@ export function Tile ({ data, favicon, title, id, prefix }) {
tabindex={0}
role="button"
href={data}
data-item-state={state.type}
data-id={id}
data-index={index}
data-edge={'closestEdge' in state && state.closestEdge}
ref={ref}
style={{ viewTransitionName: prefix + id }}
>
<div class={cn(styles.icon, styles.draggable)}>
{/* <span class={styles.favicon} style={{backgroundColor: favicon}}/> */}
<img src={favicon} loading="lazy" class={styles.favicon} alt={`favicon for ${title}`} />
{visible && <img src={favicon} loading="lazy" class={styles.favicon} alt={`favicon for ${title}`} />}
</div>
<div class={styles.text}>
{title}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,115 +1,8 @@
import { Mocks } from './mocks.js'
import { perPlatform } from '../../../injected/integration-test/type-helpers.mjs'
import { join } from 'node:path'
import { expect } from '@playwright/test'

/**
* @typedef {import('../../../injected/integration-test/type-helpers.mjs').Build} Build
* @typedef {import('../../../injected/integration-test/type-helpers.mjs').PlatformInfo} PlatformInfo
*/

export class NewtabPage {
/**
* @param {import("@playwright/test").Page} page
* @param {Build} build
* @param {PlatformInfo} platform
*/
constructor (page, build, platform) {
this.page = page
this.build = build
this.platform = platform
this.mocks = new Mocks(page, build, platform, {
context: 'specialPages',
featureName: 'newTabPage',
env: 'development'
})
this.favorites = new FavoritesPage(this)
this.page.on('console', console.log)
// default mocks - just enough to render the first page without error
this.mocks.defaultResponses({
requestSetAsDefault: {},
requestImport: {},
/** @type {import('../../types/new-tab.js').InitialSetupResponse} */
initialSetup: {
widgets: [
{ id: 'favorites' },
{ id: 'privacyStats' }
],
widgetConfigs: [
{ id: 'favorites', visibility: 'visible' },
{ id: 'privacyStats', visibility: 'visible' }
],
env: 'development',
locale: 'en',
platform: {
name: this.platform.name || 'windows'
}
},
stats_getConfig: {},
stats_getData: {},
widgets_setConfig: {}
})
}

/**
* Opens a page with optional parameters.
* This method ensures that mocks are installed and routes are set up before navigating to the page.
*
* @param {Object} [params] - Optional parameters for opening the page.
* @param {'debug' | 'production'} [params.mode] - Optional parameters for opening the page.
* @param {boolean} [params.willThrow] - Optional flag to simulate an exception
*/
async openPage ({ mode = 'debug', willThrow = false } = { }) {
await this.mocks.install()
await this.page.route('/**', (route, req) => {
const url = new URL(req.url())
// try to serve assets, but change `/` to 'index'
let filepath = url.pathname
if (filepath === '/') filepath = 'index.html'

return route.fulfill({
status: 200,
path: join(this.basePath, filepath)
})
})
const searchParams = new URLSearchParams({ mode, willThrow: String(willThrow) })
await this.page.goto('/' + '?' + searchParams.toString())
}

/**
* We test the fully built artifacts, so for each test run we need to
* select the correct HTML file.
* @return {string}
*/
get basePath () {
return this.build.switch({
windows: () => '../build/windows/pages/new-tab',
integration: () => '../build/integration/pages/new-tab'
})
}

/**
* @param {import("@playwright/test").Page} page
* @param {import("@playwright/test").TestInfo} testInfo
*/
static create (page, testInfo) {
// Read the configuration object to determine which platform we're testing against
const { platformInfo, build } = perPlatform(testInfo.project.use)
return new NewtabPage(page, build, platformInfo)
}

async reducedMotion () {
await this.page.emulateMedia({ reducedMotion: 'reduce' })
}

async darkMode () {
await this.page.emulateMedia({ colorScheme: 'dark' })
}
}

class FavoritesPage {
export class FavoritesPage {
/**
* @param {NewtabPage} ntp
* @param {import("../../../integration-tests/new-tab.page.js").NewtabPage} ntp
*/
constructor (ntp) {
this.ntp = ntp
Expand Down Expand Up @@ -239,6 +132,16 @@ class FavoritesPage {
}, number, { timeout: 2000 })
}

async tabsPastEmptyFavorites () {
const { page } = this.ntp
const body = page.locator('body')
await body.press('Tab')
await body.press('Tab')
const statsToggle = page.getByLabel('Hide recent activity')
const isActive = await statsToggle.evaluate(handle => handle === document.activeElement)
expect(isActive).toBe(true)
}

/**
* Retrieves the nth favorite item from the Favorites section on the current page.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { test, expect } from '@playwright/test'
import { NewtabPage } from './page-objects/newtab'
import { NewtabPage } from '../../../integration-tests/new-tab.page.js'
import { FavoritesPage } from './favorites.page.js'

test.describe('newtab favorites', () => {
test('fetches config + favorites data', async ({ page }, workerInfo) => {
const ntp = NewtabPage.create(page, workerInfo)
await ntp.reducedMotion()
await ntp.openPage()

const calls1 = await ntp.mocks.waitForCallCount({ method: 'initialSetup', count: 1 })
const calls2 = await ntp.mocks.waitForCallCount({ method: 'favorites_getConfig', count: 1 })
const calls3 = await ntp.mocks.waitForCallCount({ method: 'favorites_getData', count: 1 })
Expand All @@ -16,48 +18,60 @@ test.describe('newtab favorites', () => {
})
test('Toggles expansion', async ({ page }, workerInfo) => {
const ntp = NewtabPage.create(page, workerInfo)
const favorites = new FavoritesPage(ntp)
await ntp.reducedMotion()
await ntp.openPage()
await ntp.favorites.togglesExpansion()
await favorites.togglesExpansion()
})
test('Adds an item', async ({ page }, workerInfo) => {
const ntp = NewtabPage.create(page, workerInfo)
const favorites = new FavoritesPage(ntp)
await ntp.reducedMotion()
await ntp.openPage()
await ntp.favorites.addsAnItem()
await favorites.addsAnItem()
})
test('Opens context menu', async ({ page }, workerInfo) => {
const ntp = NewtabPage.create(page, workerInfo)
const favorites = new FavoritesPage(ntp)
await ntp.reducedMotion()
await ntp.openPage()
await ntp.favorites.rightClickInvokesContextMenuFor()
await favorites.rightClickInvokesContextMenuFor()
})
test('Supports keyboard nav', async ({ page }, workerInfo) => {
const ntp = NewtabPage.create(page, workerInfo)
const favorites = new FavoritesPage(ntp)
await ntp.reducedMotion()
await ntp.openPage()
await ntp.favorites.tabsThroughItems()
await favorites.tabsThroughItems()
})
test('re-orders items', async ({ page }, workerInfo) => {
test('initial empty state', async ({ page }, workerInfo) => {
const ntp = NewtabPage.create(page, workerInfo)
const favorites = new FavoritesPage(ntp)
await ntp.reducedMotion()
await ntp.openPage()
await ntp.favorites.drags({ index: 0, to: 2 })
await ntp.openPage({ favoritesCount: 0 })
await favorites.tabsPastEmptyFavorites()
})
})

test.describe('newtab privacy stats', () => {
test('fetches config + stats', async ({ page }, workerInfo) => {
test('re-orders items', async ({ page }, workerInfo) => {
const ntp = NewtabPage.create(page, workerInfo)
const favorites = new FavoritesPage(ntp)
await ntp.reducedMotion()
await ntp.openPage()

const calls1 = await ntp.mocks.waitForCallCount({ method: 'initialSetup', count: 1 })
const calls2 = await ntp.mocks.waitForCallCount({ method: 'stats_getData', count: 1 })
const calls3 = await ntp.mocks.waitForCallCount({ method: 'stats_getConfig', count: 1 })

expect(calls1.length).toBe(1)
expect(calls2.length).toBe(1)
expect(calls3.length).toBe(1)
await favorites.drags({ index: 0, to: 2 })
})
})

// test.describe('newtab privacy stats', () => {
// test('fetches config + stats', async ({ page }, workerInfo) => {
// const ntp = NewtabPage.create(page, workerInfo)
// await ntp.reducedMotion()
// await ntp.openPage()
//
// const calls1 = await ntp.mocks.waitForCallCount({ method: 'initialSetup', count: 1 })
// const calls2 = await ntp.mocks.waitForCallCount({ method: 'stats_getData', count: 1 })
// const calls3 = await ntp.mocks.waitForCallCount({ method: 'stats_getConfig', count: 1 })
//
// expect(calls1.length).toBe(1)
// expect(calls2.length).toBe(1)
// expect(calls3.length).toBe(1)
// })
// })
Loading

0 comments on commit ed057dc

Please sign in to comment.