Skip to content

Commit

Permalink
Merge pull request #5241 from hallieswan/SWC-6557
Browse files Browse the repository at this point in the history
  • Loading branch information
hallieswan authored Dec 11, 2023
2 parents b984c54 + c19cdca commit 629b3b7
Show file tree
Hide file tree
Showing 12 changed files with 1,213 additions and 21 deletions.
6 changes: 5 additions & 1 deletion e2e/discussions.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,11 @@ test.describe('Discussions', () => {
})

await testAuth.step('Dismiss alert', async () => {
await dismissAlert(validatedUserPage, 'A reply has been edited.')
// handle case where alert has auto-closed before the test gets to this step
const alertText = 'A reply has been edited.'
if (await validatedUserPage.getByText(alertText).isVisible()) {
await dismissAlert(validatedUserPage, alertText)
}
})

await expectThreadReplyVisible(
Expand Down
2 changes: 1 addition & 1 deletion e2e/files.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ const dismissFileUploadAlert = async (page: Page) => {
const getFileMD5 = async (page: Page) => {
return await testAuth.step('get file MD5', async () => {
const row = page.getByRole('row').filter({ hasText: 'MD5' })
expect(row.getByRole('cell')).toHaveCount(2)
await expect(row.getByRole('cell')).toHaveCount(2)
return row.getByRole('cell').filter({ hasNotText: 'MD5' }).textContent()
})
}
Expand Down
6 changes: 5 additions & 1 deletion e2e/fixtures/authenticatedUserPages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,11 @@ export const testAuth = base.extend<

const cleanupPage = await createPage(browser)
for (const userId of userIds) {
await cleanupTestUser(userId, cleanupPage)
try {
await cleanupTestUser(userId, cleanupPage)
} catch (error) {
console.error(`Failed to clean up userId: ${userId}:\n${error}`)
}
}
await cleanupPage.close()
},
Expand Down
53 changes: 53 additions & 0 deletions e2e/helpers/dates.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { Page, test } from '@playwright/test'

const getDecadeIndex = (year: number) => Math.floor(year / 10)
export const selectDateUsingGoogUiDatePicker = async (
page: Page,
year: number,
month: string,
day: number,
) => {
const decade = getDecadeIndex(year)
const currentDecade = getDecadeIndex(new Date().getFullYear())
const decadeDifference = currentDecade - decade

const switchButton = page.locator('.switch:visible')

await test.step('select year', async () => {
await switchButton.click()
await switchButton.click()

if (decadeDifference !== 0) {
await test.step('change decade', async () => {
const arrowSelector =
decadeDifference > 0 ? '.prev:visible' : '.next:visible'
for (let i = 0; i < decadeDifference; i++) {
await page.locator(arrowSelector).click()
}
})
}

await page
.getByText(year.toString(), { exact: true })
.locator('visible=true')
.click()
})

await test.step('select month', async () => {
await page.getByText(month, { exact: true }).locator('visible=true').click()
})

await test.step('select day', async () => {
let dayLocator = page.getByRole('cell', {
name: day.toString(),
exact: true,
})

// handle case where day may also be visible in previous or next month
// e.g. 1 or 30
if ((await dayLocator.count()) > 1) {
dayLocator = day > 15 ? dayLocator.nth(1) : dayLocator.first()
}
await dayLocator.click()
})
}
4 changes: 3 additions & 1 deletion e2e/helpers/entities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import { BackendDestinationEnum, doDelete, doGet } from './http'
import { FileHandle, FileType, FileUploadComplete } from './types'
import { waitForSrcEndpointConfig } from './utils'

export function generateEntityName(entityType: 'project' | 'folder' | 'file') {
export function generateEntityName(
entityType: 'project' | 'folder' | 'file' | 'table',
) {
return `swc-e2e-${entityType}-entity-${uuidv4()}`
}

Expand Down
298 changes: 298 additions & 0 deletions e2e/helpers/tableColumnSchemaEditor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,298 @@
import { Locator, Page, expect, test } from '@playwright/test'

// Types -----------------------------------------------------------------------
type FacetType = 'Range' | 'Values'
export type ColumnSchemaConfig = {
name: string
type: string
size?: number
maxListLength?: number
defaultValue?: string
restrictValues?: string[]
facet?: FacetType
}
type ColumnSchemaKey = keyof ColumnSchemaConfig
const columnNameToSchemaKey: Record<string, ColumnSchemaKey | string> = {
'Column ID': 'id',
'Column Name': 'name',
'Column Type': 'type',
Size: 'size',
'Max List Length': 'maxListLength',
'Default Value': 'defaultValue',
'Restrict Values': 'restrictValues',
Facet: 'facet',
}

// Set Individual Column Schema ------------------------------------------------
const enterColumnName = async (
page: Page,
columnIndex: number,
columnName: string,
) => {
await test.step('enter column name', async () => {
const columnNameFields = page.getByPlaceholder('Column name')
await expect(columnNameFields).toHaveCount(columnIndex + 1)
await columnNameFields.nth(columnIndex).fill(columnName)
})
}

const selectColumnType = async (
page: Page,
columnIndex: number,
columnType: string,
) => {
await test.step('select column type', async () => {
const columnTypeFields = page.getByLabel('Column Type')
await columnTypeFields.nth(columnIndex).click()
await page.getByRole('option', { name: columnType, exact: true }).click()
})
}

const setColumnMaxSize = async (
page: Page,
columnIndex: number,
maxSize: number,
) => {
await test.step('set column size', async () => {
const columnMaxSizeFields = page.getByLabel('Maximum Size')
await columnMaxSizeFields.nth(columnIndex).fill(maxSize.toString())
})
}

const setColumnMaxListLength = async (
page: Page,
columnIndex: number,
maxListLength: number,
) => {
await test.step('set column max list length', async () => {
const columnMaxListLengthFields = page.getByLabel('Maximum List Length')
await columnMaxListLengthFields
.nth(columnIndex)
.fill(maxListLength.toString())
})
}

const setColumnDefaultValue = async (
page: Page,
columnIndex: number,
defaultValue: string,
columnType: string,
) => {
await test.step('set column default value', async () => {
const columnDefaultValueField = page
.getByLabel('Default Value')
.nth(columnIndex)
if (columnType === 'Boolean') {
await columnDefaultValueField.click()
await page.getByRole('option', { name: defaultValue }).click()
} else {
await columnDefaultValueField.fill(defaultValue)
}
})
}

const enterColumnRestrictValues = async (
page: Page,
columnIndex: number,
values: string[],
) => {
await test.step('enter restrict values', async () => {
await page.getByLabel('Restrict Values').nth(columnIndex).click()

const editValuesHeader = page.getByText('Edit Values')
await expect(editValuesHeader).toBeVisible()

await page.getByRole('button', { name: 'Paste new values' }).click()
await page
.getByPlaceholder('Place comma or tab delimited values here')
.fill(values.join(','))
await page.getByRole('button', { name: 'Add', exact: true }).click()
await page.getByRole('button', { name: 'OK' }).click()

await expect(editValuesHeader).not.toBeVisible()
})
}

const selectColumnFacet = async (
page: Page,
columnIndex: number,
facetType: FacetType,
) => {
await test.step('set column facet', async () => {
const columnFacetFields = page.getByLabel('Facet Type')
await columnFacetFields.nth(columnIndex).click()
await page.getByRole('option', { name: facetType }).click()
})
}

// Set Table Schema ------------------------------------------------------------
const enterColumnSchema = async (
page: Page,
columnConfig: ColumnSchemaConfig,
columnIndex: number,
) => {
if (columnConfig.name) {
await enterColumnName(page, columnIndex, columnConfig.name)
}
if (columnConfig.type) {
await selectColumnType(page, columnIndex, columnConfig.type)
}
if (columnConfig.size) {
await setColumnMaxSize(page, columnIndex, columnConfig.size)
}
if (columnConfig.maxListLength) {
await setColumnMaxListLength(page, columnIndex, columnConfig.maxListLength)
}
if (columnConfig.defaultValue) {
await setColumnDefaultValue(
page,
columnIndex,
columnConfig.defaultValue,
columnConfig.type,
)
}
if (columnConfig.restrictValues) {
await enterColumnRestrictValues(
page,
columnIndex,
columnConfig.restrictValues,
)
}
if (columnConfig.facet) {
await selectColumnFacet(page, columnIndex, columnConfig.facet)
}
}

export const addColumnsFromColumnSchemaConfig = async (
page: Page,
columnConfigs: ColumnSchemaConfig[],
) => {
await test.step('add columns from column schema config', async () => {
const addColumnButton = page.getByRole('button', {
name: 'Add Column',
})

const columnStartingIndex =
await test.step('calculate starting index', async () => {
return await page.getByPlaceholder('Column name').count()
})

for (
let columnIndex = columnStartingIndex;
columnIndex < columnConfigs.length;
columnIndex++
) {
const columnConfig = columnConfigs[columnIndex]
await test.step(`add column: ${columnConfig.name}`, async () => {
await addColumnButton.click()
await enterColumnSchema(page, columnConfig, columnIndex)
})
}
})
}

// Validate Table Column Schema ------------------------------------------------
const expectColumnSchemaKeyMatchesConfig = async (
columnSchemaConfig: ColumnSchemaConfig,
columnSchemaKey: ColumnSchemaKey,
schemaCell: Locator,
) => {
if (columnSchemaConfig[columnSchemaKey]) {
await test.step(`confirm column schema key: ${columnSchemaKey}`, async () => {
let expectedValues = columnSchemaConfig[columnSchemaKey]!

// handle display of types that contain spaces
// e.g. 'String List' -> 'StringList'
if (columnSchemaKey === 'type') {
expectedValues = expectedValues.toString().replace(' ', '')
}

// handle display of restrict values
if (
columnSchemaKey === 'restrictValues' &&
Array.isArray(expectedValues)
) {
expectedValues = expectedValues.join(', ')
}

await expect(schemaCell).toHaveText(expectedValues.toString())
})
}
}

const expectColumnSchemaMatchesConfig = async (
columnSchemaConfig: ColumnSchemaConfig,
schemaCells: Locator,
columnSchemaIndexMap: Record<ColumnSchemaKey, number>,
) => {
await test.step(`confirm column schema: ${columnSchemaConfig.name}`, async () => {
for (const columnSchemaKey in columnSchemaConfig) {
const columnSchemaIndex = columnSchemaIndexMap[columnSchemaKey]

await expectColumnSchemaKeyMatchesConfig(
columnSchemaConfig,
columnSchemaKey as ColumnSchemaKey,
schemaCells.nth(columnSchemaIndex),
)
}
})
}

const expectAllColumnSchemasMatchConfig = async (
columnsSchemaConfig: ColumnSchemaConfig[],
schemaRows: Locator,
columnSchemaIndexMap: Record<ColumnSchemaKey, number>,
) => {
for (let i = 0; i < columnsSchemaConfig.length; i++) {
// skip the first row, which contains the table headers
const schemaCells = schemaRows.nth(i + 1).getByRole('cell')
await expectColumnSchemaMatchesConfig(
columnsSchemaConfig[i],
schemaCells,
columnSchemaIndexMap,
)
}
}

export const openTableColumnSchema = async (page: Page) => {
await test.step('open table column schema', async () => {
await page.getByRole('button', { name: 'Show Table Schema' }).click()
await expect(page.getByText('Schema', { exact: true })).toBeVisible()
})
}

const getTableColumnSchemaRows = async (page: Page) => {
return page.getByRole('table').filter({ hasText: 'Column' }).getByRole('row')
}

export const expectTableSchemaCorrect = async (
page: Page,
columnsSchemaConfig: ColumnSchemaConfig[],
) => {
await test.step('table has expected schema', async () => {
await openTableColumnSchema(page)
const schemaRows = await getTableColumnSchemaRows(page)

const columnSchemaIndexMap =
await test.step('confirm schema column names', async () => {
const columnNamesExpected = Object.keys(columnNameToSchemaKey)
let columnSchemaIndexMap: Partial<Record<ColumnSchemaKey, number>> = {}
const columnNames = schemaRows.nth(0).getByRole('cell')
for (let i = 0; i < columnNamesExpected.length; i++) {
// the initial column is empty in UI, so start from 1
const columnName = await columnNames.nth(i + 1).textContent()
expect(columnName).toBe(columnNamesExpected[i])
columnSchemaIndexMap[columnNameToSchemaKey[columnName!]] = i + 1
}
return columnSchemaIndexMap as Record<ColumnSchemaKey, number>
})

await test.step('confirm schema rows', async () => {
await expectAllColumnSchemasMatchConfig(
columnsSchemaConfig,
schemaRows,
columnSchemaIndexMap,
)
})
})
}
Loading

0 comments on commit 629b3b7

Please sign in to comment.