Skip to content

Commit

Permalink
refactor: filter tests page object model (#885)
Browse files Browse the repository at this point in the history
Related to #832 

This PR refactors the filter tests to use the page object model. The
tests are a bit more involved than the `metadataDrawer`, so this PR also
introduces an `actor` layer that aggregates steps from the `page object`
layer. The goal is to have each test contain a readable set of steps -
enough that a new reader can easily understand what is being tested, but
not so much that it's overwhelming. There should be clear places to look
for what's happening under the hood.
  • Loading branch information
ehoops-cz authored Jul 24, 2024
1 parent 68c8c7f commit df26183
Show file tree
Hide file tree
Showing 15 changed files with 989 additions and 14 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import type { ApolloClient, NormalizedCacheObject } from '@apollo/client'
import type {
ApolloClient,
ApolloQueryResult,
NormalizedCacheObject,
} from '@apollo/client'

import { gql } from 'app/__generated__'
import { Runs_Bool_Exp } from 'app/__generated__/graphql'
import { GetDatasetByIdQuery, Runs_Bool_Exp } from 'app/__generated__/graphql'
import { MAX_PER_PAGE } from 'app/constants/pagination'
import { FilterState, getFilterState } from 'app/hooks/useFilter'
import { getTiltRangeFilter } from 'app/utils/filter'
Expand Down Expand Up @@ -225,7 +229,7 @@ export async function getDatasetById({
id: number
page?: number
params?: URLSearchParams
}) {
}): Promise<ApolloQueryResult<GetDatasetByIdQuery>> {
return client.query({
query: GET_DATASET_BY_ID,
variables: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import type { ApolloClient, NormalizedCacheObject } from '@apollo/client'
import type {
ApolloClient,
ApolloQueryResult,
NormalizedCacheObject,
} from '@apollo/client'
import { URLSearchParams } from 'url'

import { gql } from 'app/__generated__'
import {
Annotation_Files_Bool_Exp,
Annotations_Bool_Exp,
GetRunByIdQuery,
} from 'app/__generated__/graphql'
import { MAX_PER_PAGE } from 'app/constants/pagination'
import { FilterState, getFilterState } from 'app/hooks/useFilter'
Expand Down Expand Up @@ -345,7 +350,7 @@ export async function getRunById({
id: number
page?: number
params?: URLSearchParams
}) {
}): Promise<ApolloQueryResult<GetRunByIdQuery>> {
return client.query({
query: GET_RUN_BY_ID_QUERY,
variables: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { test } from '@playwright/test'
import { getApolloClient } from 'e2e/apollo'
import { TableValidator } from 'e2e/pageObjects/filters/types'

import { QueryParams } from 'app/constants/query'

import { TableValidator } from './types'
import { goTo } from './utils'

export function testGroundTruthAnnotationFilter({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { ApolloClient, NormalizedCacheObject } from '@apollo/client'
import { test } from '@playwright/test'
import { getApolloClient } from 'e2e/apollo'
import { E2E_CONFIG } from 'e2e/constants'
import { TableValidator } from 'e2e/pageObjects/filters/types'

import { QueryParams } from 'app/constants/query'

import { TableValidator } from './types'
import { goTo } from './utils'

export interface MultiInputFilter {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { ApolloClient, NormalizedCacheObject } from '@apollo/client'
import { Page, test } from '@playwright/test'
import { getApolloClient } from 'e2e/apollo'
import { TableValidator } from 'e2e/pageObjects/filters/types'
import { identity } from 'lodash-es'

import { QueryParams } from 'app/constants/query'

import { TableValidator } from './types'
import { goTo } from './utils'

async function openDropdown(page: Page, label: string) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Page, test } from '@playwright/test'
import { getApolloClient } from 'e2e/apollo'
import { TableValidator } from 'e2e/pageObjects/filters/types'
import { identity } from 'lodash-es'

import { QueryParams } from 'app/constants/query'

import { TableValidator } from './types'
import { goTo } from './utils'

async function openFilterDropdown(page: Page, label: string) {
Expand Down Expand Up @@ -35,7 +35,7 @@ export function testSingleSelectFilter({
}: {
label: string
queryParam: QueryParams
serialize?(value: string): string
serialize?: (value: string) => string
url: string
validateTable: TableValidator
values: string[]
Expand Down
3 changes: 1 addition & 2 deletions frontend/packages/data-portal/e2e/filters/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { expect, Page, test } from '@playwright/test'
import { E2E_CONFIG, translations } from 'e2e/constants'
import { TableValidatorOptions } from 'e2e/pageObjects/filters/types'

import {
GetDatasetByIdQuery,
Expand All @@ -11,8 +12,6 @@ import { getBrowseDatasets } from 'app/graphql/getBrowseDatasets.server'
import { getDatasetById } from 'app/graphql/getDatasetById.server'
import { getRunById } from 'app/graphql/getRunById.server'

import { TableValidatorOptions } from './types'

async function waitForTableCountChange({
countLabel,
expectedFilterCount,
Expand Down
188 changes: 188 additions & 0 deletions frontend/packages/data-portal/e2e/pageObjects/filters/filtersActor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
import { ApolloClient, NormalizedCacheObject } from '@apollo/client'
import { E2E_CONFIG, translations } from 'e2e/constants'

import { GetRunByIdQuery } from 'app/__generated__/graphql'
import { QueryParams } from 'app/constants/query'
import { getRunById } from 'app/graphql/getRunById.server'

import { FiltersPage } from './filtersPage'
import { MultiInputFilterType } from './types'
import {
getAnnotationRowCountFromData,
getExpectedFilterCount,
getExpectedTotalCount,
getExpectedUrlWithQueryParams,
} from './utils'

export class FiltersActor {
private filtersPage: FiltersPage

constructor(filtersPage: FiltersPage) {
this.filtersPage = filtersPage
}

// #region Navigation
public async goToFilteredUrl({
baseUrl,
paramObject,
}: {
baseUrl: string
paramObject: Record<string, string>
}) {
const url = this.filtersPage.getFilteredUrl({ baseUrl, paramObject })
await this.filtersPage.goTo(url.href)
}
// #endregion Navigation

// #region Data
public async getSingleRunDataWithParams({
client,
id,
pageNumber,
url,
queryParamKey,
queryParamValue,
serialize,
}: {
client: ApolloClient<NormalizedCacheObject>
id: number
pageNumber?: number
url: string
queryParamKey?: QueryParams
queryParamValue: string
serialize?: (value: string) => string
}) {
const { params } = getExpectedUrlWithQueryParams({
url,
queryParamKey,
queryParamValue,
serialize,
})

const { data } = await getRunById({
client,
params,
id,
page: pageNumber,
})

return data
}
// #endregion Data

// #region Macro
public async addSingleSelectFilter({
label,
value,
}: {
label: string
value: string
}) {
await this.filtersPage.openFilterDropdown(label)
await this.filtersPage.selectFilterOption(value)
}

public async addMultiInputFilter({
buttonLabel,
filter,
hasMultipleFilters,
}: {
buttonLabel: string
filter: MultiInputFilterType
hasMultipleFilters: boolean
}) {
await this.filtersPage.openFilterDropdown(buttonLabel)
await this.filtersPage.fillInputFilter({
label: `${filter.label}${hasMultipleFilters ? ':' : ''}`,
value: E2E_CONFIG[filter.value] as string,
})
await this.filtersPage.applyMultiInputFilter()
}

// #endregion Macro

// #region Validate
public async expectUrlQueryParamsToBeCorrect({
url,
queryParamKey,
queryParamValue,
serialize,
}: {
url: string
queryParamKey?: QueryParams
queryParamValue: string
serialize?: (value: string) => string
}) {
const { expectedUrl } = getExpectedUrlWithQueryParams({
url,
queryParamKey,
queryParamValue,
serialize,
})

await this.filtersPage.expectNavigationToMatch(expectedUrl.href)
}

public async expectAnnotationsTableToBeCorrect({
singleRunData,
}: {
singleRunData: GetRunByIdQuery
}) {
// Extract expectedFilterCount and expectedTotalCount from data
const expectedFilterCount = getExpectedFilterCount({ singleRunData })
const expectedTotalCount = getExpectedTotalCount({ singleRunData })

// Wait for table subtitle to be correct: `^${expectedFilterCount} of ${expectedTotalCount} ${countLabel}$`
await this.filtersPage.waitForTableCountChange({
countLabel: translations.annotations,
expectedFilterCount,
expectedTotalCount,
})

// Validate rows
// Get all annotation ids from the expected data
const annotationRowCountFromData = getAnnotationRowCountFromData({
singleRunData,
})
// Get all annotation ids from the table
const annotationRowCountFromTable =
await this.filtersPage.getAnnotationRowCountFromTable()

// Ensure all annotation ids from the expected data are in the table
this.filtersPage.expectRowCountsToMatch(
annotationRowCountFromData,
annotationRowCountFromTable,
)
}

public async expectDataAndAnnotationsTableToMatch({
client,
id,
pageNumber,
url,
queryParamKey,
queryParamValue,
serialize,
}: {
client: ApolloClient<NormalizedCacheObject>
id: number
pageNumber?: number
url: string
queryParamKey?: QueryParams
queryParamValue: string
serialize?: (value: string) => string
}) {
const singleRunData = await this.getSingleRunDataWithParams({
client,
id,
pageNumber,
url,
queryParamKey,
queryParamValue,
serialize,
})

await this.expectAnnotationsTableToBeCorrect({ singleRunData })
}
// #endregion Validate
}
Loading

0 comments on commit df26183

Please sign in to comment.