From 61e0c719233076f3105b0881aa41ce3ec733fc40 Mon Sep 17 00:00:00 2001 From: Aref Shafaei Date: Fri, 22 Sep 2023 17:21:59 -0700 Subject: [PATCH] create separate catalog for each project + update ermrestdatautils --- .github/workflows/e2e.yml | 6 +- docs/dev-docs/e2e-test.md | 2 + package-lock.json | 14 +-- package.json | 2 +- test/playwright/locators/record.ts | 32 +++++ .../setup/playwright.configuration.ts | 6 +- test/playwright/setup/playwright.import.ts | 71 +++++------ .../playwright/setup/playwright.parameters.ts | 35 +++++- test/playwright/setup/playwright.setup.ts | 118 ++++++++++++------ test/playwright/setup/playwright.teardown.ts | 14 ++- .../navbar/no-logo.spec.ts | 8 +- .../all-features/navbar/base-config.spec.ts | 5 +- .../record/related-table.config.ts | 7 ++ .../all-features/record/related-table.spec.ts | 90 +++++++++++++ .../navbar/catalog-chaise-config.spec.ts | 7 +- test/playwright/utils/locators.ts | 14 --- test/playwright/utils/record-utils.ts | 39 ++++++ 17 files changed, 341 insertions(+), 129 deletions(-) create mode 100644 test/playwright/locators/record.ts create mode 100644 test/playwright/specs/all-features/record/related-table.config.ts create mode 100644 test/playwright/specs/all-features/record/related-table.spec.ts delete mode 100644 test/playwright/utils/locators.ts create mode 100644 test/playwright/utils/record-utils.ts diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 19c164775..ada59e578 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -20,7 +20,7 @@ jobs: timeout-minutes: 120 steps: - name: Checkout repository code - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: path: chaise - name: Setup the system @@ -56,10 +56,6 @@ jobs: sudo pip3 install globus_sdk sudo pip3 install psycopg2-binary sudo pip3 install oauth2client - - name: Set up JDK 1.8 - uses: actions/setup-java@v1 - with: - java-version: 1.8 - name: Install webauthn run: | sudo useradd -m -r webauthn diff --git a/docs/dev-docs/e2e-test.md b/docs/dev-docs/e2e-test.md index 321e3b7e5..befd0cf9f 100644 --- a/docs/dev-docs/e2e-test.md +++ b/docs/dev-docs/e2e-test.md @@ -176,6 +176,8 @@ TODO ``` page.pause(); + +npx playwright test --config CONFIG_LOC --project=NAME_OF_PROEJECT --debug ``` ## Writing test diff --git a/package-lock.json b/package-lock.json index 9a26f1c2b..25e3e0f31 100644 --- a/package-lock.json +++ b/package-lock.json @@ -47,7 +47,7 @@ "webpack-cli": "^4.9.2" }, "devDependencies": { - "@isrd-isi-edu/ermrest-data-utils": "0.0.2", + "@isrd-isi-edu/ermrest-data-utils": "0.0.3", "@playwright/test": "^1.34.2", "@typescript-eslint/eslint-plugin": "^5.13.0", "@typescript-eslint/parser": "^5.13.0", @@ -1770,9 +1770,9 @@ "dev": true }, "node_modules/@isrd-isi-edu/ermrest-data-utils": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/@isrd-isi-edu/ermrest-data-utils/-/ermrest-data-utils-0.0.2.tgz", - "integrity": "sha512-x1pdGiqkCUwazRSEo/LWQ/b4nYD11+qKXqbV5vhOBym5WcM/EQjHoLTzG27Pa73CIpTblEumh5988MzdnUTk1w==", + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@isrd-isi-edu/ermrest-data-utils/-/ermrest-data-utils-0.0.3.tgz", + "integrity": "sha512-EWL1TwYxC9xCOo1zv7oWtLbQn2jhVt0toLApsl+IEOvZG3bxuGoZpk0A84aOHdtn/sFp0rzuZj/Zi7HdfIPlWQ==", "dev": true, "dependencies": { "axios": "^0.24.0", @@ -12827,9 +12827,9 @@ "dev": true }, "@isrd-isi-edu/ermrest-data-utils": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/@isrd-isi-edu/ermrest-data-utils/-/ermrest-data-utils-0.0.2.tgz", - "integrity": "sha512-x1pdGiqkCUwazRSEo/LWQ/b4nYD11+qKXqbV5vhOBym5WcM/EQjHoLTzG27Pa73CIpTblEumh5988MzdnUTk1w==", + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@isrd-isi-edu/ermrest-data-utils/-/ermrest-data-utils-0.0.3.tgz", + "integrity": "sha512-EWL1TwYxC9xCOo1zv7oWtLbQn2jhVt0toLApsl+IEOvZG3bxuGoZpk0A84aOHdtn/sFp0rzuZj/Zi7HdfIPlWQ==", "dev": true, "requires": { "axios": "^0.24.0", diff --git a/package.json b/package.json index eea6757aa..f16c4e2f6 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "ui" ], "devDependencies": { - "@isrd-isi-edu/ermrest-data-utils": "0.0.2", + "@isrd-isi-edu/ermrest-data-utils": "0.0.3", "@playwright/test": "^1.34.2", "@typescript-eslint/eslint-plugin": "^5.13.0", "@typescript-eslint/parser": "^5.13.0", diff --git a/test/playwright/locators/record.ts b/test/playwright/locators/record.ts new file mode 100644 index 000000000..05944ec42 --- /dev/null +++ b/test/playwright/locators/record.ts @@ -0,0 +1,32 @@ +import { makeSafeIdAttr } from '@isrd-isi-edu/chaise/src/utils/string-utils'; +import { Locator, Page } from '@playwright/test'; + +export default class RecordLocators { + static async waitForRecordPageReady(page: Page) { + await RecordLocators.getEntityTitleElement(page).waitFor({ state: 'visible' }); + await RecordLocators.getMainSectionTable(page).waitFor({ state: 'visible' }); + await RecordLocators.getRelatedSectionSpinner(page).waitFor({ state: 'detached' }) + } + + static getEntityTitleElement(page: Page): Locator { + return page.locator('.entity-title'); + } + + static getMainSectionTable(page: Page): Locator { + return page.locator('.record-main-section-table'); + } + + static getRelatedSectionSpinner(page: Page): Locator { + return page.locator('.related-section-spinner'); + } + + static getRelatedTableAccordion(page: Page, displayname: string): Locator { + displayname = makeSafeIdAttr(displayname); + return page.locator(`#rt-heading-${displayname}`); + } + + static getRelatedTableAccordionContent(page: Page, displayname: string): Locator { + const acc = RecordLocators.getRelatedTableAccordion(page, displayname); + return acc.locator('.accordion-collapse'); + } +} diff --git a/test/playwright/setup/playwright.configuration.ts b/test/playwright/setup/playwright.configuration.ts index ad6811338..abebb7d33 100644 --- a/test/playwright/setup/playwright.configuration.ts +++ b/test/playwright/setup/playwright.configuration.ts @@ -3,7 +3,7 @@ import { resolve } from 'path'; import os from 'os'; import { TestOptions } from '@isrd-isi-edu/chaise/test/playwright/setup/playwright.model'; -import { STORAGE_STATE } from '@isrd-isi-edu/chaise/test/playwright/setup/playwright.parameters'; +import { STORAGE_STATE, PRESET_PROJECT_NAME } from '@isrd-isi-edu/chaise/test/playwright/setup/playwright.parameters'; const getConfig = (options: TestOptions) => { @@ -39,7 +39,7 @@ const getConfig = (options: TestOptions) => { retries: 0, // Opt out of parallel tests on CI. - workers: process.env.CI ? 1 : undefined, + // workers: process.env.CI ? 1 : undefined, // Reporter to use reporter: process.env.CI ? [ @@ -63,7 +63,7 @@ const getConfig = (options: TestOptions) => { // Configure projects for major browsers. projects: [ { - name: 'pretest', + name: PRESET_PROJECT_NAME, testDir: __dirname, testMatch: 'playwright.pretest.ts' }, diff --git a/test/playwright/setup/playwright.import.ts b/test/playwright/setup/playwright.import.ts index 0270441e4..f0a5e6701 100644 --- a/test/playwright/setup/playwright.import.ts +++ b/test/playwright/setup/playwright.import.ts @@ -1,56 +1,31 @@ import axios from 'axios'; +import { getCatalogID } from '@isrd-isi-edu/chaise/test/playwright/setup/playwright.parameters'; +import { isObjectAndNotNull } from '@isrd-isi-edu/chaise/src/utils/type-utils'; + // eslint-disable-next-line @typescript-eslint/no-var-requires const ermrestUtils = require('@isrd-isi-edu/ermrest-data-utils'); - -export async function setupCatalog(schemaConfigurations: any) { +/** + * create a catalog object based on the given setup object + * Resolved promise returns the created rows and the id of catalog. + */ +export async function setupCatalog(setup: { catalog: any, schemas: any }): Promise<{ entities: any, catalogId: string }> { return new Promise((resolve, reject) => { - // merge all the schema configurations - const catalog: any = {}, schemas: any = {}; - - schemaConfigurations.forEach((config: any) => { - // copy annotations and ACLs over to the submitted catalog object - if (config.catalog && typeof config.catalog === 'object') { - if (!('acls' in config.catalog)) { - config.catalog['acls'] = { 'select': ['*'] }; - } - - // if empty object, this loop is skipped - for (const prop in config.catalog) { - // if property is set already - if (catalog[prop]) { - console.log(`${prop} is already defined on catalog object, overriding previously set value with new one`); - } - catalog[prop] = config.catalog[prop]; - } - } - - schemas[config.schema.name] = { - path: config.schema.path - }; - - if (config.entities) { - schemas[config.schema.name].entities = config.entities.path; - } - }); - // create the settings based on the acceptable strucutre. // please refer to ermest data utils documentation for the strucutre const settings = { url: process.env.ERMREST_URL, authCookie: process.env.AUTH_COOKIE, - setup: { catalog, schemas } + setup: setup }; // NOTE do we want ot allow this? // reuse the same catalogid // if (catalogId) settings.setup.catalog.id = catalogId; - axios.get(process.env.ERMREST_URL!).then((res) => { - return ermrestUtils.createSchemasAndEntities(settings); - }).then(function (data) { + ermrestUtils.createSchemasAndEntities(settings).then(function (data: any) { const entities: any = {}; if (data.schemas) { @@ -69,20 +44,20 @@ export async function setupCatalog(schemaConfigurations: any) { console.log('Attached entities for the schemas'); } resolve({ entities: entities, catalogId: data.catalogId }); - }).catch(function (err) { + }).catch(function (err: any) { console.log('error while trying to create model and data:'); console.log(err); reject(err); }); - }); - } +/** + * remove a given catalog + */ export async function removeCatalog(catalogId: string) { return new Promise(async (resolve, reject) => { try { - console.log('remove catalog called!'); await ermrestUtils.tear({ // NOTE this setup is needed by ermrest-data-utils // we should refactor ermrest-data-utils to not need this @@ -93,8 +68,6 @@ export async function removeCatalog(catalogId: string) { catalogId: catalogId, authCookie: process.env.AUTH_COOKIE }); - - console.log(`catalog ${catalogId} removed.`); resolve(true); } catch (exp) { console.log(`Unable to remove catalog ${catalogId}`); @@ -103,6 +76,22 @@ export async function removeCatalog(catalogId: string) { }); } +/** + * Remove all the catalogs based on the env variable + */ +export async function removeAllCatalogs() { + const catalogIds = getCatalogID(); + if (isObjectAndNotNull(catalogIds)) { + for (const p in catalogIds) { + await removeCatalog(catalogIds[p]); + console.log(`Catalog deleted with id ${catalogIds[p]} for project ${p}`); + } + } else { + console.log('Catalog information is missing (either not created or invalid env variable).') + } + return true; +} + /** * Delete the given list of namespaces diff --git a/test/playwright/setup/playwright.parameters.ts b/test/playwright/setup/playwright.parameters.ts index 43c23360b..e735f4b8e 100644 --- a/test/playwright/setup/playwright.parameters.ts +++ b/test/playwright/setup/playwright.parameters.ts @@ -1,13 +1,41 @@ +import { isObjectAndNotNull } from '@isrd-isi-edu/chaise/src/utils/type-utils'; import { resolve } from 'path'; export const ERMREST_URL = process.env.ERMREST_URL; /** - * return the catalog created for tests + * return the catalog created for tests. + * + * YOU MUST CALL THIS WITH A PROJECT NAME IF YOU WANT TO GET THE STRING RESULT + * * (populated during setup) */ -export const getCatalogID = () : string => { - return process.env.CATALOG_ID!; +export const getCatalogID = (projectName?: string, dontLogError?: boolean) : string | any | null => { + try { + const obj = JSON.parse(process.env.CATALOG_ID!); + if (!isObjectAndNotNull(obj) || (projectName && !obj[projectName])) { + throw new Error(''); + } + return projectName ? obj[projectName] : obj; + } catch (exp) { + if (!dontLogError) { + console.error(exp); + console.log('existing CATALOG_ID env variable value is not valid.'); + } + } + + return null; +} + +export const setCatalogID = (projectName: string, catalogId: string) => { + let curr = getCatalogID(undefined, true); + if (isObjectAndNotNull(curr)) { + curr[projectName] = catalogId; + } else { + curr = {}; + curr[projectName] = catalogId; + } + process.env.CATALOG_ID = JSON.stringify(curr); } @@ -23,6 +51,7 @@ export const getCatalogID = () : string => { */ export const ENTITIES_PATH = 'entities.json'; +export const PRESET_PROJECT_NAME = 'pretest'; /** * return the session object for the main user (catalog owner). diff --git a/test/playwright/setup/playwright.setup.ts b/test/playwright/setup/playwright.setup.ts index 1c210b6d0..634b01822 100644 --- a/test/playwright/setup/playwright.setup.ts +++ b/test/playwright/setup/playwright.setup.ts @@ -4,8 +4,8 @@ import { execSync } from 'child_process'; import fs from 'fs'; import { TestOptions } from '@isrd-isi-edu/chaise/test/playwright/setup/playwright.model'; -import { removeCatalog, setupCatalog } from '@isrd-isi-edu/chaise/test/playwright/setup/playwright.import'; -import { ENTITIES_PATH, getCatalogID } from '@isrd-isi-edu/chaise/test/playwright/setup/playwright.parameters'; +import { removeAllCatalogs, setupCatalog } from '@isrd-isi-edu/chaise/test/playwright/setup/playwright.import'; +import { ENTITIES_PATH, PRESET_PROJECT_NAME, setCatalogID } from '@isrd-isi-edu/chaise/test/playwright/setup/playwright.parameters'; /** * @@ -43,6 +43,12 @@ export default async function globalSetup(config: FullConfig) { throw new Error('config file is empty.'); } + const projectNames: string[] = []; + config.projects.forEach((p) => { + if (p.name === PRESET_PROJECT_NAME) return; + projectNames.push(p.name); + }); + // see if the user/pass or cookie are valid try { @@ -61,7 +67,7 @@ export default async function globalSetup(config: FullConfig) { // create the catalog try { - await createCatalog(testConfiguration, options.manualTestConfig); + await createCatalog(testConfiguration, projectNames, options.manualTestConfig); } catch (exp) { throw exp; } @@ -112,9 +118,9 @@ async function checkUserSessions(): Promise<{ session: any, authCookie: string } /** * create the catalog and data */ -async function createCatalog(testConfiguration: any, isManual?: boolean) { +async function createCatalog(testConfiguration: any, projectNames: string[], isManual?: boolean) { - return new Promise((resolve, reject) => { + return new Promise(async (resolve, reject) => { testConfiguration.setup.url = process.env.ERMREST_URL; testConfiguration.setup.authCookie = testConfiguration.authCookie; @@ -136,34 +142,68 @@ async function createCatalog(testConfiguration: any, isManual?: boolean) { } } - setupCatalog(schemaConfigurations).then((data: any) => { - process.env.CATALOG_ID = data.catalogId; - if (data.entities) { - const entities = data.entities; - fs.writeFile(ENTITIES_PATH, JSON.stringify(entities), 'utf8', function (err) { - if (err) { - console.log('couldn\'t write entities.'); - console.log(err); - reject(new Error('Unable to import data')); - } else { - console.log('created entities file for schemas'); - resolve(true); - } - }); + // merge all the schema configurations + const catalog: any = {}, schemas: any = {}; - } else { - resolve(true); + schemaConfigurations.forEach((config: any) => { + // copy annotations and ACLs over to the submitted catalog object + if (config.catalog && typeof config.catalog === 'object') { + if (!('acls' in config.catalog)) { + config.catalog['acls'] = { 'select': ['*'] }; + } + + // if empty object, this loop is skipped + for (const prop in config.catalog) { + // if property is set already + if (catalog[prop]) { + console.log(`${prop} is already defined on catalog object, overriding previously set value with new one`); + } + catalog[prop] = config.catalog[prop]; + } } - }, function (err) { - console.log(err); - reject(new Error('Unable to import data')); + schemas[config.schema.name] = { + path: config.schema.path + }; + + if (config.entities) { + schemas[config.schema.name].entities = config.entities.path; + } }); + for (const p of projectNames) { + try { + const res = await setupCatalog({ catalog, schemas }); + console.log(`catalog with id ${res.catalogId} created for project ${p}`); + setCatalogID(p, res.catalogId); + + // TODO capture entities per project + // const entities = res.entities; + // fs.writeFile(ENTITIES_PATH, JSON.stringify(entities), 'utf8', function (err) { + // if (err) { + // console.log('couldn\'t write entities.'); + // console.log(err); + // reject(new Error('Unable to import data')); + // } else { + // console.log('created entities file for schemas'); + // resolve(true); + // } + // }); + + } catch (exp) { + console.log(exp); + reject(new Error('Unable to import data')); + return; + } + } + + resolve(true); + }); } + /** * send a request with the given cookie to authn and retreive the session object. */ @@ -279,40 +319,38 @@ function registerCallbacks(testConfiguration: any) { // If an uncaught exception is caught then simply call cleanup // to remove the created schema/catalog/tables if catalogId is not null process.on('uncaughtException', function (err) { - const catalogId = getCatalogID(); - console.log(`in error : catalogId ${catalogId}`); console.dir(err); + console.log('uncaughtException: going to remove all catalogs.'); const cb = () => { console.error((new Date).toUTCString() + ' uncaughtException:', err.message); console.error(err.stack); process.exit(1) }; - if (!catalogDeleted && testConfiguration.cleanup && catalogId != null) { - removeCatalog(catalogId).then(cb, cb); + if (catalogDeleted) { + console.log('catalogs are already deleted'); + cb(); + return; + } + + if (testConfiguration.cleanup) { + catalogDeleted = true; + removeAllCatalogs().then(cb, cb); } else { cb(); } }); - process.on('SIGINT', function (code) { - console.log('sigint!!!'); - - const catalogId = process.env.CATALOG_ID; - if (!catalogId) { - console.log('no catalog id was set.'); - return; - } - + process.on('SIGINT', function () { if (!catalogDeleted) { catalogDeleted = true; - console.log('About to exit because of SIGINT (ctrl + c)'); - removeCatalog(catalogId).then(function () { + console.log('SIGINT: going to remove all catalogs.'); + removeAllCatalogs().then(function () { process.exit(1); }); } else { - console.log('catalog already deleted.') + console.log('SIGINT: catalogs are already deleted.'); process.exit(1); } }); diff --git a/test/playwright/setup/playwright.teardown.ts b/test/playwright/setup/playwright.teardown.ts index f49f9842d..9f89889ca 100644 --- a/test/playwright/setup/playwright.teardown.ts +++ b/test/playwright/setup/playwright.teardown.ts @@ -2,8 +2,8 @@ import { FullConfig } from '@playwright/test'; import fs from 'fs'; import { TestOptions } from '@isrd-isi-edu/chaise/test/playwright/setup/playwright.model'; -import { removeCatalog } from '@isrd-isi-edu/chaise/test/playwright/setup/playwright.import'; -import { ENTITIES_PATH, getCatalogID } from '@isrd-isi-edu/chaise/test/playwright/setup/playwright.parameters'; +import { removeAllCatalogs } from '@isrd-isi-edu/chaise/test/playwright/setup/playwright.import'; +import { ENTITIES_PATH } from '@isrd-isi-edu/chaise/test/playwright/setup/playwright.parameters'; async function globalTeardown(config: FullConfig) { /** @@ -26,18 +26,20 @@ async function globalTeardown(config: FullConfig) { } } + // TODO // if (testConfiguration.hatracNamespaces && testConfiguration.hatracNamespaces.length > 0) { // // cleanup the hatrac namespaces // promises.push(pImport.deleteHatracNamespaces(testConfiguration.authCookie, testConfiguration.hatracNamespaces)); // } - const catalogId = getCatalogID(); - if (testConfiguration.cleanup && testConfiguration.setup && catalogId != null) { - await removeCatalog(catalogId); + // remove the created catalogs + if (testConfiguration.cleanup && testConfiguration.setup) { + await removeAllCatalogs(); } + // TODO // delete the entities file - fs.unlinkSync(ENTITIES_PATH); + // fs.unlinkSync(ENTITIES_PATH); } diff --git a/test/playwright/specs/all-features-confirmation/navbar/no-logo.spec.ts b/test/playwright/specs/all-features-confirmation/navbar/no-logo.spec.ts index 4d45eb87d..1fed275d1 100644 --- a/test/playwright/specs/all-features-confirmation/navbar/no-logo.spec.ts +++ b/test/playwright/specs/all-features-confirmation/navbar/no-logo.spec.ts @@ -8,9 +8,9 @@ import PageLocators from '@isrd-isi-edu/chaise/test/playwright/locators/page'; import { getMainUserSessionObject, getCatalogID } from '@isrd-isi-edu/chaise/test/playwright/setup/playwright.parameters'; test.describe('Navbar', () => { - const PAGE_URL = `/recordset/#${getCatalogID()}/product-navbar:accommodation`; - test.beforeEach(async ({ page, baseURL }) => { + test.beforeEach(async ({ page, baseURL }, testInfo) => { + const PAGE_URL = `/recordset/#${getCatalogID(testInfo.project.name)}/product-navbar:accommodation`; await page.goto(`${baseURL}${PAGE_URL}`); }) @@ -79,7 +79,7 @@ test.describe('Navbar', () => { }); }); - test('menu support', async ({ page, context }) => { + test('menu support', async ({ page, context }, testInfo) => { const menu = NavbarLocators.getMenu(page); await test.step('should generate the correct # of list items', async () => { @@ -114,7 +114,7 @@ test.describe('Navbar', () => { // check that clicking opens the link const newPage = await PageLocators.clickNewTabLink(datasetOption, context); - await newPage.waitForURL(`**/chaise/recordset/#${getCatalogID()}/isa:dataset**`); + await newPage.waitForURL(`**/chaise/recordset/#${getCatalogID(testInfo.project.name)}/isa:dataset**`); await newPage.close(); }); } diff --git a/test/playwright/specs/all-features/navbar/base-config.spec.ts b/test/playwright/specs/all-features/navbar/base-config.spec.ts index 2d8614fcb..df72d3dfe 100644 --- a/test/playwright/specs/all-features/navbar/base-config.spec.ts +++ b/test/playwright/specs/all-features/navbar/base-config.spec.ts @@ -8,9 +8,10 @@ import ModalLocators from '@isrd-isi-edu/chaise/test/playwright/locators/modal'; import { getMainUserSessionObject, getCatalogID } from '@isrd-isi-edu/chaise/test/playwright/setup/playwright.parameters'; test.describe('Navbar', () => { - const PAGE_URL = `/recordset/#${getCatalogID()}/product-navbar:accommodation`; - test.beforeEach(async ({ page, baseURL }) => { + + test.beforeEach(async ({ page, baseURL }, testInfo) => { + const PAGE_URL = `/recordset/#${getCatalogID(testInfo.project.name)}/product-navbar:accommodation`; await page.goto(`${baseURL}${PAGE_URL}`); }) diff --git a/test/playwright/specs/all-features/record/related-table.config.ts b/test/playwright/specs/all-features/record/related-table.config.ts new file mode 100644 index 000000000..cad23102e --- /dev/null +++ b/test/playwright/specs/all-features/record/related-table.config.ts @@ -0,0 +1,7 @@ +import getConfig from '@isrd-isi-edu/chaise/test/playwright/setup/playwright.configuration'; + +export default getConfig({ + testName: 'all-features/related-table', + configFileName: 'record/related-table/dev.json', + chaiseConfigFilePath: 'test/e2e/specs/all-features/chaise-config.js', +}); diff --git a/test/playwright/specs/all-features/record/related-table.spec.ts b/test/playwright/specs/all-features/record/related-table.spec.ts new file mode 100644 index 000000000..26f2b4e9d --- /dev/null +++ b/test/playwright/specs/all-features/record/related-table.spec.ts @@ -0,0 +1,90 @@ +import { test, expect } from '@playwright/test'; + +// locators +import RecordLocators from '@isrd-isi-edu/chaise/test/playwright/locators/record'; + +// utils +import { getCatalogID } from '@isrd-isi-edu/chaise/test/playwright/setup/playwright.parameters'; + +const testParams = { + schemaName: 'product-unordered-related-tables-links', + table_name: 'accommodation', + key: { + name: 'id', + value: '2004', + operator: '=' + }, + headers: [ + 'booking', // normal + 'schedule', // has search + 'media', // has row_markdown_pattern + 'association_table', // association + 'accommodation_image', // association with page_size + 'association_table_markdown', // association with markdown + 'related_table_2', // related entity with path length 3, wait_for entity set but no markdown patt + 'table_w_aggregates', // related entity with aggregate columns + 'table_w_invalid_row_markdown_pattern', // related entity with invalid row_markdown_pattern + 'inbound related with display.wait_for entityset', //related entity with wait_for entityset and markdown patt + 'inbound related with display.wait_for agg', //related entity with wait_for agg and markdown pattern + 'inbound related with filter on main table', // related entity with filter on main table + 'inbound related with filter on related table', // related entity with filter on related table + 'association with filter on main table', + 'association with filter on related table', // association with filter on related table + 'path of length 3 with filters' // path of length 3 with filters + ], + tocHeaders: [ + 'Summary', 'booking (6)', 'schedule (2)', 'media (1)', 'association_table (1)', + 'accommodation_image (2+)', 'association_table_markdown (1)', 'related_table_2 (1)', + 'table_w_aggregates (2)', 'table_w_invalid_row_markdown_pattern (1)', + 'inbound related with display.wait_for entityset (3)', + 'inbound related with display.wait_for agg (3)', + 'inbound related with filter on main table (6)', + 'inbound related with filter on related table (1)', + 'association with filter on main table (1)', + 'association with filter on related table (1)', + 'path of length 3 with filters (1)' + ], + relatedTables: [ + + ], + scrollToDisplayname: 'table_w_aggregates' +}; + +test.describe('Related tables', () => { + + test.beforeEach(async ({ page, baseURL }, testInfo) => { + const keys = []; + keys.push(testParams.key.name + testParams.key.operator + testParams.key.value); + const PAGE_URL = `/record/#${getCatalogID(testInfo.project.name)}/${testParams.schemaName}:${testParams.table_name}/${keys.join('&')}`; + + await page.goto(`${baseURL}${PAGE_URL}`); + + await RecordLocators.waitForRecordPageReady(page); + }); + + // TODO + +}); + +test.describe('Scroll to query parameter', () => { + test('after page load should scroll to the related table', async ({ page, baseURL }, testInfo) => { + const keys = []; + keys.push(testParams.key.name + testParams.key.operator + testParams.key.value); + const PAGE_URL = `/record/#${getCatalogID(testInfo.project.name)}/${testParams.schemaName}:${testParams.table_name}/${keys.join('&')}`; + + await page.goto(`${baseURL}${PAGE_URL}?scrollTo=${testParams.scrollToDisplayname}`); + + await RecordLocators.waitForRecordPageReady(page); + + const heading = RecordLocators.getRelatedTableAccordionContent(page, testParams.scrollToDisplayname); + + // make sure it exists + await heading.waitFor({ state: 'visible' }); + + // make sure it scrolls into view + await expect(heading).toBeInViewport(); + + // make sure it is open + await expect(heading).toHaveClass(/show/); + }); +}); diff --git a/test/playwright/specs/delete-prohibited/navbar/catalog-chaise-config.spec.ts b/test/playwright/specs/delete-prohibited/navbar/catalog-chaise-config.spec.ts index f07035dc7..245423e7e 100644 --- a/test/playwright/specs/delete-prohibited/navbar/catalog-chaise-config.spec.ts +++ b/test/playwright/specs/delete-prohibited/navbar/catalog-chaise-config.spec.ts @@ -9,13 +9,13 @@ import RecordsetLocators from '@isrd-isi-edu/chaise/test/playwright/locators/rec import { getCatalogID } from '@isrd-isi-edu/chaise/test/playwright/setup/playwright.parameters'; test.describe('Navbar', () => { - const PAGE_URL = `/recordset/#${getCatalogID()}/catalog-config-navbar:config-table`; - test('when navbar is visible', async ({ page, baseURL, context }) => { + test('when navbar is visible', async ({ page, baseURL, context }, testInfo) => { const navbar = NavbarLocators.getContainer(page); const loginMenuOption = navbar.locator('.login-menu-options'); await test.step('navbar should be visible on load.', async () => { + const PAGE_URL = `/recordset/#${getCatalogID(testInfo.project.name)}/catalog-config-navbar:config-table`; await page.goto(`${baseURL}${PAGE_URL}`); await navbar.waitFor({ state: 'visible' }); }); @@ -49,7 +49,8 @@ test.describe('Navbar', () => { } }); - test('should hide the navbar bar if the hideNavbar query parameter is set to true', async ({ page, baseURL }) => { + test('should hide the navbar bar if the hideNavbar query parameter is set to true', async ({ page, baseURL }, testInfo) => { + const PAGE_URL = `/recordset/#${getCatalogID(testInfo.project.name)}/catalog-config-navbar:config-table`; await page.goto(`${baseURL}${PAGE_URL}?hideNavbar=true`); await RecordsetLocators.waitForRecordsetPageReady(page); diff --git a/test/playwright/utils/locators.ts b/test/playwright/utils/locators.ts deleted file mode 100644 index bd0411ba6..000000000 --- a/test/playwright/utils/locators.ts +++ /dev/null @@ -1,14 +0,0 @@ -export default class NavbarLocators { - static getBanner(key: string, page: any) { - const prefix = 'chaise-navbar-banner-container'; - return page.locator(`.${prefix}${key ? (prefix + '-key') : ''}`); - } - - static getTitle (page: any) { - return page.locator('#brand-text'); - } - - static getBrandImage(page: any) { - return page.locator('#brand-image') - } -} diff --git a/test/playwright/utils/record-utils.ts b/test/playwright/utils/record-utils.ts new file mode 100644 index 000000000..5f55a1d53 --- /dev/null +++ b/test/playwright/utils/record-utils.ts @@ -0,0 +1,39 @@ +import { test } from '@playwright/test'; + + +type RelatedTableTestParam = { + testTitle: string, + name: string, + schemaName: string, + displayname: string, + count: number, + canEdit: boolean, + canCreate: boolean, + canDelete: boolean, + + isAssociation?: boolean, + isMarkdown?: boolean + isInline?: boolean, + isTableMode?: boolean + viewMore?: { + name: string, + displayname: string, + filter?: string, + }, + rowValues?: string[], + rowViewPaths?: string[], + markdownValue?: string, + /** + * default 25 + */ + page_size?: number, + testAdd?: boolean, + testEdit?: boolean, + testDelete?: boolean, +}; + +export const testRelatedTable = (params: RelatedTableTestParam) => { + test(params.testTitle, async ({page}) => { + // + }) +}