Browse Source

fix(test): Integrated postgres to CI with sharding and removed other tests from CI for the timebeing

pull/4278/head
Muhammed Mustafa 2 years ago
parent
commit
3e1f7f22e4
  1. 20
      .github/workflows/ci-cd.yml
  2. 9
      .github/workflows/playwright-test-workflow.yml
  3. 2
      packages/nocodb/package.json
  4. 33
      packages/nocodb/src/lib/services/test/TestResetService/index.ts
  5. 50
      packages/nocodb/src/lib/services/test/TestResetService/resetMysqlSakilaProject.ts
  6. 115
      packages/nocodb/src/lib/services/test/TestResetService/resetPgSakilaProject.ts
  7. 110
      scripts/playwright/fixtures/expectedBaseDownloadDataPg.txt
  8. 267
      scripts/playwright/package-lock.json
  9. 9
      scripts/playwright/package.json
  10. 25
      scripts/playwright/pages/Dashboard/Settings/Metadata.ts
  11. 5
      scripts/playwright/pages/Dashboard/common/Toolbar/ViewMenu.ts
  12. 4
      scripts/playwright/quickTests/commonTest.ts
  13. 15
      scripts/playwright/scripts/docker-compose-playwright-pg.yml
  14. 20
      scripts/playwright/setup/db.ts
  15. 23
      scripts/playwright/setup/index.ts
  16. 12
      scripts/playwright/tests/columnFormula.spec.ts
  17. 1
      scripts/playwright/tests/columnRelationalExtendedTests.spec.ts
  18. 2
      scripts/playwright/tests/erd.spec.ts
  19. 4
      scripts/playwright/tests/import.spec.ts
  20. 97
      scripts/playwright/tests/metaSync.spec.ts
  21. 13
      scripts/playwright/tests/viewGridShare.spec.ts
  22. 10
      scripts/playwright/tests/viewKanban.spec.ts
  23. 4
      scripts/playwright/tests/viewMenu.spec.ts

20
.github/workflows/ci-cd.yml

@ -66,13 +66,25 @@ jobs:
# - name: run unit tests # - name: run unit tests
# working-directory: ./packages/nocodb # working-directory: ./packages/nocodb
# run: npm run test:unit # run: npm run test:unit
playwright-mysql: # playwright-mysql:
# if: ${{ github.event_name == 'push' || contains(github.event.pull_request.labels.*.name, 'trigger-CI') || !github.event.pull_request.draft }}
# uses: ./.github/workflows/playwright-test-workflow.yml
# with:
# db: mysql
# playwright-sqlite:
# if: ${{ github.event_name == 'push' || contains(github.event.pull_request.labels.*.name, 'trigger-CI') || !github.event.pull_request.draft }}
# uses: ./.github/workflows/playwright-test-workflow.yml
# with:
# db: sqlite
playwright-pg-shard-1:
if: ${{ github.event_name == 'push' || contains(github.event.pull_request.labels.*.name, 'trigger-CI') || !github.event.pull_request.draft }} if: ${{ github.event_name == 'push' || contains(github.event.pull_request.labels.*.name, 'trigger-CI') || !github.event.pull_request.draft }}
uses: ./.github/workflows/playwright-test-workflow.yml uses: ./.github/workflows/playwright-test-workflow.yml
with: with:
db: mysql db: pg
playwright-sqlite: shard: 1
playwright-pg-shard-2:
if: ${{ github.event_name == 'push' || contains(github.event.pull_request.labels.*.name, 'trigger-CI') || !github.event.pull_request.draft }} if: ${{ github.event_name == 'push' || contains(github.event.pull_request.labels.*.name, 'trigger-CI') || !github.event.pull_request.draft }}
uses: ./.github/workflows/playwright-test-workflow.yml uses: ./.github/workflows/playwright-test-workflow.yml
with: with:
db: sqlite db: pg
shard: 2

9
.github/workflows/playwright-test-workflow.yml

@ -6,6 +6,7 @@ on:
db: db:
required: true required: true
type: string type: string
shard: string
jobs: jobs:
playwright: playwright:
@ -57,7 +58,7 @@ jobs:
- name: setup pg - name: setup pg
if: ${{ inputs.db == 'pg' }} if: ${{ inputs.db == 'pg' }}
working-directory: ./ working-directory: ./
run: docker-compose -f ./scripts/playwright/scripts/docker-compose-pg.yml up -d & run: docker-compose -f ./scripts/playwright/scripts/docker-compose-playwright-pg.yml up -d &
- name: run frontend - name: run frontend
working-directory: ./packages/nc-gui working-directory: ./packages/nc-gui
run: npm run ci:run run: npm run ci:run
@ -86,16 +87,16 @@ jobs:
done done
- name: Run Playwright tests - name: Run Playwright tests
working-directory: ./scripts/playwright working-directory: ./scripts/playwright
run: E2E_DB_TYPE=${{ inputs.db }} npm run ci:test run: E2E_DB_TYPE=${{ inputs.db }} npm run ci:test:shard:${{ inputs.shard }}
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v3
if: always() if: always()
with: with:
name: playwright-report name: playwright-report-${{ inputs.db }}-${{ inputs.shard }}
path: ./scripts/playwright/playwright-report/ path: ./scripts/playwright/playwright-report/
retention-days: 2 retention-days: 2
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v3
if: always() if: always()
with: with:
name: backend logs name: backend-logs-${{ inputs.db }}-${{ inputs.shard }}
path: ./packages/nocodb/mysql_test_backend.log path: ./packages/nocodb/mysql_test_backend.log
retention-days: 2 retention-days: 2

2
packages/nocodb/package.json

@ -36,7 +36,7 @@
"docker:build": "EE=\"true-xc-test\" webpack --config docker/webpack.config.js", "docker:build": "EE=\"true-xc-test\" webpack --config docker/webpack.config.js",
"watch:build": "nodemon -e ts,js -w ./src -x npm run build", "watch:build": "nodemon -e ts,js -w ./src -x npm run build",
"watch:run": "cross-env NC_DISABLE_TELE1=true EE=true nodemon -e ts,js -w ./src -x \"ts-node src/run/docker --log-error --project tsconfig.json\"", "watch:run": "cross-env NC_DISABLE_TELE1=true EE=true nodemon -e ts,js -w ./src -x \"ts-node src/run/docker --log-error --project tsconfig.json\"",
"watch:run:playwright": "cross-env PLAYWRIGHT_TEST=true NC_DISABLE_TELE=true EE=true nodemon -e ts,js -w ./src -x \"ts-node src/run/testDocker --log-error --project tsconfig.json\"", "watch:run:playwright": "cross-env DATABASE_URL=sqlite:./test_noco.db PLAYWRIGHT_TEST=true NC_DISABLE_TELE=true EE=true nodemon -e ts,js -w ./src -x \"ts-node src/run/testDocker --log-error --project tsconfig.json\"",
"watch:run:playwright:quick": "rm -f ./test_noco.db; cp ../../scripts/cypress/fixtures/quickTest/noco_0_91_7.db ./test_noco.db; cross-env DATABASE_URL=sqlite:./test_noco.db NC_DISABLE_TELE=true EE=true nodemon -e ts,js -w ./src -x \"ts-node src/run/docker --log-error --project tsconfig.json\"", "watch:run:playwright:quick": "rm -f ./test_noco.db; cp ../../scripts/cypress/fixtures/quickTest/noco_0_91_7.db ./test_noco.db; cross-env DATABASE_URL=sqlite:./test_noco.db NC_DISABLE_TELE=true EE=true nodemon -e ts,js -w ./src -x \"ts-node src/run/docker --log-error --project tsconfig.json\"",
"watch:run:playwright:pg:cyquick": "cross-env EE=true nodemon -e ts,js -w ./src -x \"ts-node src/run/dockerRunPG_CyQuick.ts --log-error --project tsconfig.json\"", "watch:run:playwright:pg:cyquick": "cross-env EE=true nodemon -e ts,js -w ./src -x \"ts-node src/run/dockerRunPG_CyQuick.ts --log-error --project tsconfig.json\"",
"watch:run:cypress": "cross-env EE=true nodemon -e ts,js -w ./src -x \"ts-node src/run/docker --log-error --project tsconfig.json\"", "watch:run:cypress": "cross-env EE=true nodemon -e ts,js -w ./src -x \"ts-node src/run/docker --log-error --project tsconfig.json\"",

33
packages/nocodb/src/lib/services/test/TestResetService/index.ts

@ -9,6 +9,8 @@ import NocoCache from '../../../cache/NocoCache';
import { CacheScope } from '../../../utils/globals'; import { CacheScope } from '../../../utils/globals';
import ProjectUser from '../../../models/ProjectUser'; import ProjectUser from '../../../models/ProjectUser';
const workerStatus = {};
const loginRootUser = async () => { const loginRootUser = async () => {
const response = await axios.post( const response = await axios.post(
'http://localhost:8080/api/v1/auth/user/signin', 'http://localhost:8080/api/v1/auth/user/signin',
@ -45,6 +47,21 @@ export class TestResetService {
async process() { async process() {
try { try {
console.log(
`earlier workerStatus: parrelledId: ${this.parallelId}:`,
workerStatus[this.parallelId]
);
// wait till previous worker is done
while (workerStatus[this.parallelId] === 'processing') {
console.log(
`waiting for previous worker to finish parrelelId:${this.parallelId}`
);
await new Promise((resolve) => setTimeout(resolve, 1000));
}
workerStatus[this.parallelId] = 'processing';
const token = await loginRootUser(); const token = await loginRootUser();
const { project } = await this.resetProject({ const { project } = await this.resetProject({
@ -53,12 +70,18 @@ export class TestResetService {
parallelId: this.parallelId, parallelId: this.parallelId,
}); });
await removeAllProjectCreatedByTheTest(this.parallelId); try {
await removeAllPrefixedUsersExceptSuper(this.parallelId); await removeAllProjectCreatedByTheTest(this.parallelId);
await removeAllPrefixedUsersExceptSuper(this.parallelId);
} catch (e) {
console.log(`Error in cleaning up project: ${this.parallelId}`, e);
}
workerStatus[this.parallelId] = 'completed';
return { token, project }; return { token, project };
} catch (e) { } catch (e) {
console.error('TestResetService:process', e); console.error('TestResetService:process', e);
workerStatus[this.parallelId] = 'errored';
return { error: e }; return { error: e };
} }
} }
@ -80,9 +103,11 @@ export class TestResetService {
const bases = await project.getBases(); const bases = await project.getBases();
await Project.delete(project.id); if (bases.length > 0) {
await NcConnectionMgrv2.deleteAwait(bases[0]);
}
if (bases.length > 0) await NcConnectionMgrv2.deleteAwait(bases[0]); await Project.delete(project.id);
} }
if (dbType == 'sqlite') { if (dbType == 'sqlite') {

50
packages/nocodb/src/lib/services/test/TestResetService/resetMysqlSakilaProject.ts

@ -45,13 +45,28 @@ const isSakilaMysqlToBeReset = async (
parallelId: string, parallelId: string,
project?: Project project?: Project
) => { ) => {
const tablesInDb: Array<string> = await knex.raw( const tablesInDbInfo: Array<any> = await knex.raw(
`SELECT table_name FROM information_schema.tables WHERE table_schema = 'test_sakila_${parallelId}'` `SELECT table_name FROM information_schema.tables WHERE table_schema = 'test_sakila_${parallelId}'`
); );
const nonMetaTablesInDb = tablesInDbInfo[0]
.map((t) => t['TABLE_NAME'])
.filter((table) => table !== 'nc_evolutions');
const mysqlSakilaTablesAndViews = [
...mysqlSakilaTables,
...mysqlSakilaSqlViews,
];
if ( if (
tablesInDb.length === 0 || nonMetaTablesInDb.length === 0 ||
(tablesInDb.length > 0 && !tablesInDb.includes(`actor`)) // If there are sakila tables
!nonMetaTablesInDb.includes(`actor`) ||
// If there are no pg sakila tables in tables in db
!(
nonMetaTablesInDb.length === mysqlSakilaTablesAndViews.length &&
nonMetaTablesInDb.every((t) => mysqlSakilaTablesAndViews.includes(t))
)
) { ) {
return true; return true;
} }
@ -151,4 +166,33 @@ const resetMysqlSakilaProject = async ({
await nc_knex.destroy(); await nc_knex.destroy();
}; };
const mysqlSakilaTables = [
'actor',
'address',
'category',
'city',
'country',
'customer',
'film',
'film_text',
'film_actor',
'film_category',
'inventory',
'language',
'payment',
'rental',
'staff',
'store',
];
const mysqlSakilaSqlViews = [
'actor_info',
'customer_list',
'film_list',
'nicer_but_slower_film_list',
'sales_by_film_category',
'sales_by_store',
'staff_list',
];
export default resetMysqlSakilaProject; export default resetMysqlSakilaProject;

115
packages/nocodb/src/lib/services/test/TestResetService/resetPgSakilaProject.ts

@ -1,9 +1,9 @@
import axios from 'axios'; import axios from 'axios';
import { Knex, knex } from 'knex'; import { knex } from 'knex';
import { promises as fs } from 'fs'; import { promises as fs } from 'fs';
const util = require('util'); // const util = require('util');
const exec = util.promisify(require('child_process').exec); // const exec = util.promisify(require('child_process').exec);
import Audit from '../../../models/Audit'; import Audit from '../../../models/Audit';
import Project from '../../../models/Project'; import Project from '../../../models/Project';
@ -19,7 +19,7 @@ const config = {
multipleStatements: true, multipleStatements: true,
}, },
searchPath: ['public', 'information_schema'], searchPath: ['public', 'information_schema'],
pool: { min: 0, max: 5 }, pool: { min: 0, max: 1 },
}; };
const extMysqlProject = (title, parallelId) => ({ const extMysqlProject = (title, parallelId) => ({
@ -45,16 +45,31 @@ const extMysqlProject = (title, parallelId) => ({
external: true, external: true,
}); });
const isSakilaPgToBeReset = async (knex: Knex, project?: Project) => { const isSakilaPgToBeReset = async (parallelId: string, project?: Project) => {
const sakilaKnex = knex(sakilaKnexConfig(parallelId));
const tablesInDb: Array<string> = ( const tablesInDb: Array<string> = (
await knex.raw( await sakilaKnex.raw(
`SELECT * FROM information_schema.tables WHERE table_schema = 'public'` `SELECT * FROM information_schema.tables WHERE table_schema = 'public'`
) )
).rows.map((row) => row.table_name); ).rows.map((row) => row.table_name);
await sakilaKnex.destroy();
const nonMetaTablesInDb = tablesInDb.filter(
(table) => table !== 'nc_evolutions'
);
const pgSakilaTablesAndViews = [...pgSakilaTables, ...pgSakilaSqlViews];
if ( if (
tablesInDb.length === 0 || tablesInDb.length === 0 ||
(tablesInDb.length > 0 && !tablesInDb.includes(`actor`)) // If there are sakila tables
!tablesInDb.includes(`actor`) ||
// If there are no pg sakila tables in tables in db
!(
nonMetaTablesInDb.length === pgSakilaTablesAndViews.length &&
nonMetaTablesInDb.every((t) => pgSakilaTablesAndViews.includes(t))
)
) { ) {
return true; return true;
} }
@ -66,34 +81,33 @@ const isSakilaPgToBeReset = async (knex: Knex, project?: Project) => {
return audits?.length > 0; return audits?.length > 0;
}; };
const resetSakilaPg = async ( const resetSakilaPg = async (parallelId: string, isEmptyProject: boolean) => {
pgknex: Knex,
parallelId: string,
isEmptyProject: boolean
) => {
const testsDir = __dirname.replace( const testsDir = __dirname.replace(
'/src/lib/services/test/TestResetService', '/src/lib/services/test/TestResetService',
'/tests' '/tests'
); );
await pgknex.raw(`DROP DATABASE IF EXISTS sakila_${parallelId}`);
await pgknex.raw(`CREATE DATABASE sakila_${parallelId}`);
if (isEmptyProject) return; if (isEmptyProject) return;
const sakilaKnex = knex(sakilaKnexConfig(parallelId)); try {
const sakilaKnex = knex(sakilaKnexConfig(parallelId));
const schemaFile = await fs.readFile( const schemaFile = await fs.readFile(
`${testsDir}/pg-sakila-db/03-postgres-sakila-schema.sql` `${testsDir}/pg-sakila-db/01-postgres-sakila-schema.sql`
); );
await sakilaKnex.raw(schemaFile.toString()); await sakilaKnex.raw(schemaFile.toString());
const dataFilePath = `${testsDir}/pg-sakila-db/04-postgres-sakila-insert-data.sql`; const trx = await sakilaKnex.transaction();
await exec( const dataFile = await fs.readFile(
`export PGPASSWORD='${config.connection.password}';psql sakila_${parallelId} -h localhost -U postgres -w -f ${dataFilePath}` `${testsDir}/pg-sakila-db/02-postgres-sakila-insert-data.sql`
); );
await trx.raw(dataFile.toString());
await trx.commit();
await sakilaKnex.destroy(); await sakilaKnex.destroy();
} catch (e) {
console.error(`Error resetting pg sakila db: Worker ${parallelId}`);
throw Error(`Error resetting pg sakila db: Worker ${parallelId}`);
}
}; };
const sakilaKnexConfig = (parallelId: string) => ({ const sakilaKnexConfig = (parallelId: string) => ({
@ -123,13 +137,12 @@ const resetPgSakilaProject = async ({
await pgknex.raw(`CREATE DATABASE sakila_${parallelId}`); await pgknex.raw(`CREATE DATABASE sakila_${parallelId}`);
} catch (e) {} } catch (e) {}
const sakilaKnex = knex(sakilaKnexConfig(parallelId)); if (isEmptyProject || (await isSakilaPgToBeReset(parallelId, oldProject))) {
await pgknex.raw(`DROP DATABASE IF EXISTS sakila_${parallelId}`);
await pgknex.raw(`CREATE DATABASE sakila_${parallelId}`);
await pgknex.destroy();
if (isEmptyProject || (await isSakilaPgToBeReset(sakilaKnex, oldProject))) { await resetSakilaPg(parallelId, isEmptyProject);
await sakilaKnex.destroy();
await resetSakilaPg(pgknex, parallelId, isEmptyProject);
} else {
await sakilaKnex.destroy();
} }
const response = await axios.post( const response = await axios.post(
@ -145,8 +158,40 @@ const resetPgSakilaProject = async ({
console.error('Error creating project', response.data); console.error('Error creating project', response.data);
throw new Error('Error creating project', response.data); throw new Error('Error creating project', response.data);
} }
await pgknex.destroy();
}; };
const pgSakilaTables = [
'country',
'city',
'actor',
'film_actor',
'category',
'film_category',
'language',
'film',
'payment_p2007_01',
'payment_p2007_02',
'payment_p2007_03',
'payment_p2007_04',
'payment_p2007_05',
'payment_p2007_06',
'payment',
'customer',
'inventory',
'rental',
'address',
'staff',
'store',
];
const pgSakilaSqlViews = [
'actor_info',
'customer_list',
'film_list',
'nicer_but_slower_film_list',
'sales_by_film_category',
'sales_by_store',
'staff_list',
];
export default resetPgSakilaProject; export default resetPgSakilaProject;

110
scripts/playwright/fixtures/expectedBaseDownloadDataPg.txt

@ -0,0 +1,110 @@
Country,LastUpdate,City List
Afghanistan,"""2006-02-14T23:14:00.000Z""",Kabul
Algeria,"""2006-02-14T23:14:00.000Z""","Batna, Bchar, Skikda"
American Samoa,"""2006-02-14T23:14:00.000Z""",Tafuna
Angola,"""2006-02-14T23:14:00.000Z""","Benguela, Namibe"
Anguilla,"""2006-02-14T23:14:00.000Z""",South Hill
Argentina,"""2006-02-14T23:14:00.000Z""","Almirante Brown, Avellaneda, Baha Blanca, Crdoba, Escobar, Ezeiza, La Plata, Merlo, Quilmes, San Miguel de Tucumn, Santa F, Tandil, Vicente Lpez"
Armenia,"""2006-02-14T23:14:00.000Z""",Yerevan
Australia,"""2006-02-14T23:14:00.000Z""",Woodridge
Austria,"""2006-02-14T23:14:00.000Z""","Graz, Linz, Salzburg"
Azerbaijan,"""2006-02-14T23:14:00.000Z""","Baku, Sumqayit"
Bahrain,"""2006-02-14T23:14:00.000Z""",al-Manama
Bangladesh,"""2006-02-14T23:14:00.000Z""","Dhaka, Jamalpur, Tangail"
Belarus,"""2006-02-14T23:14:00.000Z""","Mogiljov, Molodetno"
Bolivia,"""2006-02-14T23:14:00.000Z""","El Alto, Sucre"
Brazil,"""2006-02-14T23:14:00.000Z""","Alvorada, Angra dos Reis, Anpolis, Aparecida de Goinia, Araatuba, Bag, Belm, Blumenau, Boa Vista, Braslia, Goinia, Guaruj, guas Lindas de Gois, Ibirit, Juazeiro do Norte, Juiz de Fora, Luzinia, Maring, Po, Poos de Caldas, Rio Claro, Santa Brbara dOeste, Santo Andr, So Bernardo do Campo, So Leopoldo"
Brunei,"""2006-02-14T23:14:00.000Z""",Bandar Seri Begawan
Bulgaria,"""2006-02-14T23:14:00.000Z""","Ruse, Stara Zagora"
Cambodia,"""2006-02-14T23:14:00.000Z""","Battambang, Phnom Penh"
Cameroon,"""2006-02-14T23:14:00.000Z""","Bamenda, Yaound"
Canada,"""2006-02-14T23:14:00.000Z""","Gatineau, Halifax, Lethbridge, London, Oshawa, Richmond Hill, Vancouver"
Chad,"""2006-02-14T23:14:00.000Z""",NDjamna
Chile,"""2006-02-14T23:14:00.000Z""","Antofagasta, Coquimbo, Rancagua"
China,"""2006-02-14T23:14:00.000Z""","Baicheng, Baiyin, Binzhou, Changzhou, Datong, Daxian, Dongying, Emeishan, Enshi, Ezhou, Fuyu, Fuzhou, Haining, Hami, Hohhot, Huaian, Jinchang, Jining, Jinzhou, Junan, Korla, Laiwu, Laohekou, Lengshuijiang, Leshan"
Colombia,"""2006-02-14T23:14:00.000Z""","Buenaventura, Dos Quebradas, Florencia, Pereira, Sincelejo, Sogamoso"
"Congo, The Democratic Republic of the","""2006-02-14T23:14:00.000Z""","Lubumbashi, Mwene-Ditu"
Czech Republic,"""2006-02-14T23:14:00.000Z""",Olomouc
Dominican Republic,"""2006-02-14T23:14:00.000Z""","La Romana, San Felipe de Puerto Plata, Santiago de los Caballeros"
Ecuador,"""2006-02-14T23:14:00.000Z""","Loja, Portoviejo, Robamba"
Egypt,"""2006-02-14T23:14:00.000Z""","Bilbays, Idfu, Mit Ghamr, Qalyub, Sawhaj, Shubra al-Khayma"
Estonia,"""2006-02-14T23:14:00.000Z""",Tartu
Ethiopia,"""2006-02-14T23:14:00.000Z""",Addis Abeba
Faroe Islands,"""2006-02-14T23:14:00.000Z""",Trshavn
Finland,"""2006-02-14T23:14:00.000Z""",Oulu
France,"""2006-02-14T23:14:00.000Z""","Brest, Le Mans, Toulon, Toulouse"
French Guiana,"""2006-02-14T23:14:00.000Z""",Cayenne
French Polynesia,"""2006-02-14T23:14:00.000Z""","Faaa, Papeete"
Gambia,"""2006-02-14T23:14:00.000Z""",Banjul
Germany,"""2006-02-14T23:14:00.000Z""","Duisburg, Erlangen, Halle/Saale, Mannheim, Saarbrcken, Siegen, Witten"
Greece,"""2006-02-14T23:14:00.000Z""","Athenai, Patras"
Greenland,"""2006-02-14T23:14:00.000Z""",Nuuk
Holy See (Vatican City State),"""2006-02-14T23:14:00.000Z""",Citt del Vaticano
Hong Kong,"""2006-02-14T23:14:00.000Z""",Kowloon and New Kowloon
Hungary,"""2006-02-14T23:14:00.000Z""",Szkesfehrvr
India,"""2006-02-14T23:14:00.000Z""","Adoni, Ahmadnagar, Allappuzha (Alleppey), Ambattur, Amroha, Balurghat, Berhampore (Baharampur), Bhavnagar, Bhilwara, Bhimavaram, Bhopal, Bhusawal, Bijapur, Chandrapur, Chapra, Dhule (Dhulia), Etawah, Firozabad, Gandhinagar, Gulbarga, Haldia, Halisahar, Hoshiarpur, Hubli-Dharwad, Jaipur"
Indonesia,"""2006-02-14T23:14:00.000Z""","Cianjur, Ciomas, Ciparay, Gorontalo, Jakarta, Lhokseumawe, Madiun, Pangkal Pinang, Pemalang, Pontianak, Probolinggo, Purwakarta, Surakarta, Tegal"
Iran,"""2006-02-14T23:14:00.000Z""","Arak, Esfahan, Kermanshah, Najafabad, Qomsheh, Shahr-e Kord, Sirjan, Tabriz"
Iraq,"""2006-02-14T23:14:00.000Z""",Mosul
Israel,"""2006-02-14T23:14:00.000Z""","Ashdod, Ashqelon, Bat Yam, Tel Aviv-Jaffa"
Italy,"""2006-02-14T23:14:00.000Z""","Alessandria, Bergamo, Brescia, Brindisi, Livorno, Syrakusa, Udine"
Japan,"""2006-02-14T23:14:00.000Z""","Akishima, Fukuyama, Higashiosaka, Hino, Hiroshima, Isesaki, Iwaki, Iwakuni, Iwatsuki, Izumisano, Kakamigahara, Kamakura, Kanazawa, Koriyama, Kurashiki, Kuwana, Matsue, Miyakonojo, Nagareyama, Okayama, Okinawa, Omiya, Onomichi, Otsu, Sagamihara"
Kazakstan,"""2006-02-14T23:14:00.000Z""","Pavlodar, Zhezqazghan"
Kenya,"""2006-02-14T23:14:00.000Z""","Kisumu, Nyeri"
Kuwait,"""2006-02-14T23:14:00.000Z""",Jalib al-Shuyukh
Latvia,"""2006-02-14T23:14:00.000Z""","Daugavpils, Liepaja"
Liechtenstein,"""2006-02-14T23:14:00.000Z""",Vaduz
Lithuania,"""2006-02-14T23:14:00.000Z""",Vilnius
Madagascar,"""2006-02-14T23:14:00.000Z""",Mahajanga
Malawi,"""2006-02-14T23:14:00.000Z""",Lilongwe
Malaysia,"""2006-02-14T23:14:00.000Z""","Ipoh, Kuching, Sungai Petani"
Mexico,"""2006-02-14T23:14:00.000Z""","Acua, Allende, Atlixco, Carmen, Celaya, Coacalco de Berriozbal, Coatzacoalcos, Cuauhtmoc, Cuautla, Cuernavaca, El Fuerte, Guadalajara, Hidalgo, Huejutla de Reyes, Huixquilucan, Jos Azueta, Jurez, La Paz, Matamoros, Mexicali, Monclova, Nezahualcyotl, Pachuca de Soto, Salamanca, San Felipe del Progreso"
Moldova,"""2006-02-14T23:14:00.000Z""",Chisinau
Morocco,"""2006-02-14T23:14:00.000Z""","Beni-Mellal, Nador, Sal"
Mozambique,"""2006-02-14T23:14:00.000Z""","Beira, Naala-Porto, Tete"
Myanmar,"""2006-02-14T23:14:00.000Z""","Monywa, Myingyan"
Nauru,"""2006-02-14T23:14:00.000Z""",Yangor
Nepal,"""2006-02-14T23:14:00.000Z""",Birgunj
Netherlands,"""2006-02-14T23:14:00.000Z""","Amersfoort, Apeldoorn, Ede, Emmen, s-Hertogenbosch"
New Zealand,"""2006-02-14T23:14:00.000Z""",Hamilton
Nigeria,"""2006-02-14T23:14:00.000Z""","Benin City, Deba Habe, Effon-Alaiye, Ife, Ikerre, Ilorin, Kaduna, Ogbomosho, Ondo, Owo, Oyo, Sokoto, Zaria"
North Korea,"""2006-02-14T23:14:00.000Z""",Pyongyang
Oman,"""2006-02-14T23:14:00.000Z""","Masqat, Salala"
Pakistan,"""2006-02-14T23:14:00.000Z""","Dadu, Mandi Bahauddin, Mardan, Okara, Shikarpur"
Paraguay,"""2006-02-14T23:14:00.000Z""","Asuncin, Ciudad del Este, San Lorenzo"
Peru,"""2006-02-14T23:14:00.000Z""","Callao, Hunuco, Lima, Sullana"
Philippines,"""2006-02-14T23:14:00.000Z""","Baybay, Bayugan, Bislig, Cabuyao, Cavite, Davao, Gingoog, Hagonoy, Iligan, Imus, Lapu-Lapu, Mandaluyong, Ozamis, Santa Rosa, Taguig, Talavera, Tanauan, Tanza, Tarlac, Tuguegarao"
Poland,"""2006-02-14T23:14:00.000Z""","Bydgoszcz, Czestochowa, Jastrzebie-Zdrj, Kalisz, Lublin, Plock, Tychy, Wroclaw"
Puerto Rico,"""2006-02-14T23:14:00.000Z""","Arecibo, Ponce"
Romania,"""2006-02-14T23:14:00.000Z""","Botosani, Bucuresti"
Runion,"""2006-02-14T23:14:00.000Z""",Saint-Denis
Russian Federation,"""2006-02-14T23:14:00.000Z""","Atinsk, Balaiha, Dzerzinsk, Elista, Ivanovo, Jaroslavl, Jelets, Kaliningrad, Kamyin, Kirovo-Tepetsk, Kolpino, Korolev, Kurgan, Kursk, Lipetsk, Ljubertsy, Maikop, Moscow, Nabereznyje Telny, Niznekamsk, Novoterkassk, Pjatigorsk, Serpuhov, Smolensk, Syktyvkar"
Saint Vincent and the Grenadines,"""2006-02-14T23:14:00.000Z""",Kingstown
Saudi Arabia,"""2006-02-14T23:14:00.000Z""","Abha, al-Hawiya, al-Qatif, Jedda, Tabuk"
Senegal,"""2006-02-14T23:14:00.000Z""",Ziguinchor
Slovakia,"""2006-02-14T23:14:00.000Z""",Bratislava
South Africa,"""2006-02-14T23:14:00.000Z""","Boksburg, Botshabelo, Chatsworth, Johannesburg, Kimberley, Klerksdorp, Newcastle, Paarl, Rustenburg, Soshanguve, Springs"
South Korea,"""2006-02-14T23:14:00.000Z""","Cheju, Kimchon, Naju, Tonghae, Uijongbu"
Spain,"""2006-02-14T23:14:00.000Z""","A Corua (La Corua), Donostia-San Sebastin, Gijn, Ourense (Orense), Santiago de Compostela"
Sri Lanka,"""2006-02-14T23:14:00.000Z""",Jaffna
Sudan,"""2006-02-14T23:14:00.000Z""","al-Qadarif, Omdurman"
Sweden,"""2006-02-14T23:14:00.000Z""",Malm
Switzerland,"""2006-02-14T23:14:00.000Z""","Basel, Bern, Lausanne"
Taiwan,"""2006-02-14T23:14:00.000Z""","Changhwa, Chiayi, Chungho, Fengshan, Hsichuh, Lungtan, Nantou, Tanshui, Touliu, Tsaotun"
Tanzania,"""2006-02-14T23:14:00.000Z""","Mwanza, Tabora, Zanzibar"
Thailand,"""2006-02-14T23:14:00.000Z""","Nakhon Sawan, Pak Kret, Songkhla"
Tonga,"""2006-02-14T23:14:00.000Z""",Nukualofa
Tunisia,"""2006-02-14T23:14:00.000Z""",Sousse
Turkey,"""2006-02-14T23:14:00.000Z""","Adana, Balikesir, Batman, Denizli, Eskisehir, Gaziantep, Inegl, Kilis, Ktahya, Osmaniye, Sivas, Sultanbeyli, Tarsus, Tokat, Usak"
Turkmenistan,"""2006-02-14T23:14:00.000Z""",Ashgabat
Tuvalu,"""2006-02-14T23:14:00.000Z""",Funafuti
Ukraine,"""2006-02-14T23:14:00.000Z""","Kamjanets-Podilskyi, Konotop, Mukateve, ostka, Simferopol, Sumy"
United Arab Emirates,"""2006-02-14T23:14:00.000Z""","Abu Dhabi, al-Ayn, Sharja"
United Kingdom,"""2006-02-14T23:14:00.000Z""","Bradford, Dundee, London, Southampton, Southend-on-Sea, Southport, Stockport, York"
United States,"""2006-02-14T23:14:00.000Z""","Akron, Arlington, Augusta-Richmond County, Aurora, Bellevue, Brockton, Cape Coral, Citrus Heights, Clarksville, Compton, Dallas, Dayton, El Monte, Fontana, Garden Grove, Garland, Grand Prairie, Greensboro, Joliet, Kansas City, Lancaster, Laredo, Lincoln, Manchester, Memphis"
Venezuela,"""2006-02-14T23:14:00.000Z""","Barcelona, Caracas, Cuman, Maracabo, Ocumare del Tuy, Valencia, Valle de la Pascua"
Vietnam,"""2006-02-14T23:14:00.000Z""","Cam Ranh, Haiphong, Hanoi, Nam Dinh, Nha Trang, Vinh"
"Virgin Islands, U.S.","""2006-02-14T23:14:00.000Z""",Charlotte Amalie
Yemen,"""2006-02-14T23:14:00.000Z""","Aden, Hodeida, Sanaa, Taizz"
Yugoslavia,"""2006-02-14T23:14:00.000Z""","Kragujevac, Novi Sad"
Zambia,"""2006-02-14T23:14:00.000Z""",Kitwe

267
scripts/playwright/package-lock.json generated

@ -29,6 +29,7 @@
"husky": "^8.0.1", "husky": "^8.0.1",
"lint-staged": "^13.0.3", "lint-staged": "^13.0.3",
"mysql2": "^2.3.3", "mysql2": "^2.3.3",
"pg": "^8.8.0",
"prettier": "^2.7.1", "prettier": "^2.7.1",
"promised-sqlite3": "^1.2.0" "promised-sqlite3": "^1.2.0"
} }
@ -841,6 +842,15 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/buffer-writer": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz",
"integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==",
"dev": true,
"engines": {
"node": ">=4"
}
},
"node_modules/bytes": { "node_modules/bytes": {
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
@ -3628,6 +3638,12 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/packet-reader": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz",
"integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==",
"dev": true
},
"node_modules/parent-module": { "node_modules/parent-module": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@ -3686,6 +3702,87 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/pg": {
"version": "8.8.0",
"resolved": "https://registry.npmjs.org/pg/-/pg-8.8.0.tgz",
"integrity": "sha512-UXYN0ziKj+AeNNP7VDMwrehpACThH7LUl/p8TDFpEUuSejCUIwGSfxpHsPvtM6/WXFy6SU4E5RG4IJV/TZAGjw==",
"dev": true,
"dependencies": {
"buffer-writer": "2.0.0",
"packet-reader": "1.0.0",
"pg-connection-string": "^2.5.0",
"pg-pool": "^3.5.2",
"pg-protocol": "^1.5.0",
"pg-types": "^2.1.0",
"pgpass": "1.x"
},
"engines": {
"node": ">= 8.0.0"
},
"peerDependencies": {
"pg-native": ">=3.0.1"
},
"peerDependenciesMeta": {
"pg-native": {
"optional": true
}
}
},
"node_modules/pg-connection-string": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz",
"integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==",
"dev": true
},
"node_modules/pg-int8": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
"dev": true,
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/pg-pool": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.5.2.tgz",
"integrity": "sha512-His3Fh17Z4eg7oANLob6ZvH8xIVen3phEZh2QuyrIl4dQSDVEabNducv6ysROKpDNPSD+12tONZVWfSgMvDD9w==",
"dev": true,
"peerDependencies": {
"pg": ">=8.0"
}
},
"node_modules/pg-protocol": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.5.0.tgz",
"integrity": "sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==",
"dev": true
},
"node_modules/pg-types": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
"dev": true,
"dependencies": {
"pg-int8": "1.0.1",
"postgres-array": "~2.0.0",
"postgres-bytea": "~1.0.0",
"postgres-date": "~1.0.4",
"postgres-interval": "^1.1.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/pgpass": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
"integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
"dev": true,
"dependencies": {
"split2": "^4.1.0"
}
},
"node_modules/picomatch": { "node_modules/picomatch": {
"version": "2.3.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
@ -3722,6 +3819,45 @@
"node": ">=14" "node": ">=14"
} }
}, },
"node_modules/postgres-array": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
"dev": true,
"engines": {
"node": ">=4"
}
},
"node_modules/postgres-bytea": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
"integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/postgres-date": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/postgres-interval": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
"dev": true,
"dependencies": {
"xtend": "^4.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/prelude-ls": { "node_modules/prelude-ls": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@ -4273,6 +4409,15 @@
"url": "https://github.com/chalk/slice-ansi?sponsor=1" "url": "https://github.com/chalk/slice-ansi?sponsor=1"
} }
}, },
"node_modules/split2": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz",
"integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==",
"dev": true,
"engines": {
"node": ">= 10.x"
}
},
"node_modules/sprintf-js": { "node_modules/sprintf-js": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
@ -4797,6 +4942,15 @@
"node": ">=0.8" "node": ">=0.8"
} }
}, },
"node_modules/xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
"dev": true,
"engines": {
"node": ">=0.4"
}
},
"node_modules/yallist": { "node_modules/yallist": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
@ -5394,6 +5548,12 @@
"fill-range": "^7.0.1" "fill-range": "^7.0.1"
} }
}, },
"buffer-writer": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz",
"integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==",
"dev": true
},
"bytes": { "bytes": {
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
@ -7459,6 +7619,12 @@
"aggregate-error": "^3.0.0" "aggregate-error": "^3.0.0"
} }
}, },
"packet-reader": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz",
"integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==",
"dev": true
},
"parent-module": { "parent-module": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@ -7502,6 +7668,68 @@
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
"dev": true "dev": true
}, },
"pg": {
"version": "8.8.0",
"resolved": "https://registry.npmjs.org/pg/-/pg-8.8.0.tgz",
"integrity": "sha512-UXYN0ziKj+AeNNP7VDMwrehpACThH7LUl/p8TDFpEUuSejCUIwGSfxpHsPvtM6/WXFy6SU4E5RG4IJV/TZAGjw==",
"dev": true,
"requires": {
"buffer-writer": "2.0.0",
"packet-reader": "1.0.0",
"pg-connection-string": "^2.5.0",
"pg-pool": "^3.5.2",
"pg-protocol": "^1.5.0",
"pg-types": "^2.1.0",
"pgpass": "1.x"
}
},
"pg-connection-string": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz",
"integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==",
"dev": true
},
"pg-int8": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
"dev": true
},
"pg-pool": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.5.2.tgz",
"integrity": "sha512-His3Fh17Z4eg7oANLob6ZvH8xIVen3phEZh2QuyrIl4dQSDVEabNducv6ysROKpDNPSD+12tONZVWfSgMvDD9w==",
"dev": true,
"requires": {}
},
"pg-protocol": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.5.0.tgz",
"integrity": "sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==",
"dev": true
},
"pg-types": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
"dev": true,
"requires": {
"pg-int8": "1.0.1",
"postgres-array": "~2.0.0",
"postgres-bytea": "~1.0.0",
"postgres-date": "~1.0.4",
"postgres-interval": "^1.1.0"
}
},
"pgpass": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
"integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
"dev": true,
"requires": {
"split2": "^4.1.0"
}
},
"picomatch": { "picomatch": {
"version": "2.3.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
@ -7520,6 +7748,33 @@
"integrity": "sha512-9EmeXDncC2Pmp/z+teoVYlvmPWUC6ejSSYZUln7YaP89Z6lpAaiaAnqroUt/BoLo8tn7WYShcfaCh+xofZa44Q==", "integrity": "sha512-9EmeXDncC2Pmp/z+teoVYlvmPWUC6ejSSYZUln7YaP89Z6lpAaiaAnqroUt/BoLo8tn7WYShcfaCh+xofZa44Q==",
"dev": true "dev": true
}, },
"postgres-array": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
"dev": true
},
"postgres-bytea": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
"integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==",
"dev": true
},
"postgres-date": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
"dev": true
},
"postgres-interval": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
"dev": true,
"requires": {
"xtend": "^4.0.0"
}
},
"prelude-ls": { "prelude-ls": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@ -7917,6 +8172,12 @@
"is-fullwidth-code-point": "^3.0.0" "is-fullwidth-code-point": "^3.0.0"
} }
}, },
"split2": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz",
"integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==",
"dev": true
},
"sprintf-js": { "sprintf-js": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
@ -8320,6 +8581,12 @@
"word": "~0.3.0" "word": "~0.3.0"
} }
}, },
"xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
"dev": true
},
"yallist": { "yallist": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",

9
scripts/playwright/package.json

@ -5,11 +5,15 @@
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"test": "TRACE=true npx playwright test --workers=4", "test": "TRACE=true npx playwright test --workers=4",
"test:repeat": "TRACE=true npx playwright test --workers=4 --repeat-each=10", "test:shard:1": "TRACE=true npx playwright test --workers=4 --shard=1/2",
"test:shard:2": "TRACE=true npx playwright test --workers=4 --shard=2/2",
"test:repeat": "TRACE=true npx playwright test --workers=4 --repeat-each=12",
"test:quick": "TRACE=true PW_QUICK_TEST=1 npx playwright test --workers=4", "test:quick": "TRACE=true PW_QUICK_TEST=1 npx playwright test --workers=4",
"test:debug": "./startPlayWrightServer.sh; PW_TEST_REUSE_CONTEXT=1 PW_TEST_CONNECT_WS_ENDPOINT=ws://127.0.0.1:31000/ PWDEBUG=console npx playwright test -c playwright.config.ts --headed --project=chromium --retries 0 --timeout 0 --workers 1 --max-failures=1", "test:debug": "./startPlayWrightServer.sh; PW_TEST_REUSE_CONTEXT=1 PW_TEST_CONNECT_WS_ENDPOINT=ws://127.0.0.1:31000/ PWDEBUG=console npx playwright test -c playwright.config.ts --headed --project=chromium --retries 0 --timeout 0 --workers 1 --max-failures=1",
"test:debug:quick:sqlite": "./startPlayWrightServer.sh; PW_QUICK_TEST=1 PW_TEST_REUSE_CONTEXT=1 PW_TEST_CONNECT_WS_ENDPOINT=ws://127.0.0.1:31000/ PWDEBUG=console npx playwright test -c playwright.config.ts --headed --project=chromium --retries 0 --timeout 5 --workers 1 --max-failures=1", "test:debug:quick:sqlite": "./startPlayWrightServer.sh; PW_QUICK_TEST=1 PW_TEST_REUSE_CONTEXT=1 PW_TEST_CONNECT_WS_ENDPOINT=ws://127.0.0.1:31000/ PWDEBUG=console npx playwright test -c playwright.config.ts --headed --project=chromium --retries 0 --timeout 5 --workers 1 --max-failures=1",
"ci:test": "npx playwright test --workers=2", "ci:test": "npx playwright test --workers=2",
"ci:test:shard:1": "npx playwright test --workers=2 --shard=1/2",
"ci:test:shard:2": "npx playwright test --workers=2 --shard=2/2",
"ci:test:mysql": "E2E_DB_TYPE=mysql npx playwright test --workers=2", "ci:test:mysql": "E2E_DB_TYPE=mysql npx playwright test --workers=2",
"ci:test:pg": "E2E_DB_TYPE=pg npx playwright test --workers=2" "ci:test:pg": "E2E_DB_TYPE=pg npx playwright test --workers=2"
}, },
@ -27,11 +31,12 @@
"eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-eslint-comments": "^3.2.0",
"eslint-plugin-functional": "^3.0.2", "eslint-plugin-functional": "^3.0.2",
"eslint-plugin-import": "^2.22.0", "eslint-plugin-import": "^2.22.0",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-json": "^3.1.0", "eslint-plugin-json": "^3.1.0",
"eslint-plugin-prettier": "^4.0.0",
"husky": "^8.0.1", "husky": "^8.0.1",
"lint-staged": "^13.0.3", "lint-staged": "^13.0.3",
"mysql2": "^2.3.3", "mysql2": "^2.3.3",
"pg": "^8.8.0",
"prettier": "^2.7.1", "prettier": "^2.7.1",
"promised-sqlite3": "^1.2.0" "promised-sqlite3": "^1.2.0"
}, },

25
scripts/playwright/pages/Dashboard/Settings/Metadata.ts

@ -31,18 +31,17 @@ export class MetaDataPage extends BasePage {
} }
async verifyRow({ index, model, state }: { index: number; model: string; state: string }) { async verifyRow({ index, model, state }: { index: number; model: string; state: string }) {
await expect await expect(this.get().locator(`tr.ant-table-row`).nth(index).locator(`td.ant-table-cell`).nth(0)).toHaveText(
.poll(async () => { model,
return await this.get() {
.locator(`tr.ant-table-row`) ignoreCase: true,
.nth(index) }
.locator(`td.ant-table-cell`) );
.nth(0) await expect(this.get().locator(`tr.ant-table-row`).nth(index).locator(`td.ant-table-cell`).nth(1)).toHaveText(
.textContent(); state,
}) {
.toContain(model); ignoreCase: true,
await expect( }
await this.get().locator(`tr.ant-table-row`).nth(index).locator(`td.ant-table-cell`).nth(1).textContent() );
).toContain(state);
} }
} }

5
scripts/playwright/pages/Dashboard/common/Toolbar/ViewMenu.ts

@ -55,7 +55,8 @@ export class ToolbarViewMenuPage extends BasePage {
// Get API Snippet // Get API Snippet
// ERD View // ERD View
async click({ menu, subMenu }: { menu: string; subMenu?: string }) { // todo: Move verification out of the click method
async click({ menu, subMenu, verificationInfo }: { menu: string; subMenu?: string; verificationInfo?: any }) {
await this.viewsMenuBtn.click(); await this.viewsMenuBtn.click();
await this.get().locator(`.ant-dropdown-menu-title-content:has-text("${menu}")`).first().click(); await this.get().locator(`.ant-dropdown-menu-title-content:has-text("${menu}")`).first().click();
if (subMenu) { if (subMenu) {
@ -65,7 +66,7 @@ export class ToolbarViewMenuPage extends BasePage {
downloadLocator: await this.rootPage downloadLocator: await this.rootPage
.locator(`.ant-dropdown-menu-title-content:has-text("${subMenu}")`) .locator(`.ant-dropdown-menu-title-content:has-text("${subMenu}")`)
.last(), .last(),
expectedDataFile: './fixtures/expectedBaseDownloadData.txt', expectedDataFile: verificationInfo?.verificationFile ?? './fixtures/expectedBaseDownloadData.txt',
}); });
} else { } else {
await this.rootPage.locator(`.ant-dropdown-menu-title-content:has-text("${subMenu}")`).last().click(); await this.rootPage.locator(`.ant-dropdown-menu-title-content:has-text("${subMenu}")`).last().click();

4
scripts/playwright/quickTests/commonTest.ts

@ -1,7 +1,7 @@
import { DashboardPage } from '../pages/Dashboard'; import { DashboardPage } from '../pages/Dashboard';
import { ProjectsPage } from '../pages/ProjectsPage'; import { ProjectsPage } from '../pages/ProjectsPage';
import { NcContext } from '../setup'; import { NcContext } from '../setup';
import { isMysql } from '../setup/db'; import { isMysql, isPg } from '../setup/db';
// normal fields // normal fields
const recordCells = { const recordCells = {
@ -103,7 +103,7 @@ const quickVerify = async ({
await dashboard.grid.cell.verifyVirtualCell({ await dashboard.grid.cell.verifyVirtualCell({
index: cellIndex, index: cellIndex,
columnHeader: 'Actor', columnHeader: 'Actor',
value: isMysql(context) ? ['Actor1'] : recordsVirtualCells.Actor, value: isMysql(context) || isPg(context) ? ['Actor1'] : recordsVirtualCells.Actor,
}); });
// Status (from Actor) // Status (from Actor)

15
scripts/playwright/scripts/docker-compose-playwright-pg.yml

@ -0,0 +1,15 @@
version: "2.1"
services:
pg96:
image: postgres:15
restart: always
environment:
POSTGRES_PASSWORD: password
ports:
- 5432:5432
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5

20
scripts/playwright/setup/db.ts

@ -1,5 +1,6 @@
import { NcContext } from '.'; import { NcContext } from '.';
const mysql = require('mysql2'); const mysql = require('mysql2');
const { Client } = require('pg');
import { PromisedDatabase } from 'promised-sqlite3'; import { PromisedDatabase } from 'promised-sqlite3';
const sqliteDb = new PromisedDatabase(); const sqliteDb = new PromisedDatabase();
@ -10,6 +11,23 @@ const isSqlite = (context: NcContext) => context.dbType === 'sqlite';
const isPg = (context: NcContext) => context.dbType === 'pg'; const isPg = (context: NcContext) => context.dbType === 'pg';
const pg_credentials = () => ({
user: 'postgres',
host: 'localhost',
database: `sakila_${process.env.TEST_PARALLEL_INDEX}`,
password: 'password',
port: 5432,
});
const pgExec = async (query: string) => {
// open pg client connection
const client = new Client(pg_credentials());
await client.connect();
await client.query(query);
await client.end();
};
const mysqlExec = async query => { const mysqlExec = async query => {
// creates a new mysql connection using credentials from cypress.json env's // creates a new mysql connection using credentials from cypress.json env's
const connection = mysql.createConnection({ const connection = mysql.createConnection({
@ -42,4 +60,4 @@ async function sqliteExec(query) {
await sqliteDb.close(); await sqliteDb.close();
} }
export { sqliteExec, mysqlExec, isMysql, isSqlite, isPg }; export { sqliteExec, mysqlExec, isMysql, isSqlite, isPg, pgExec };

23
scripts/playwright/setup/index.ts

@ -11,14 +11,21 @@ const setup = async ({ page, isEmptyProject }: { page: Page; isEmptyProject?: bo
let dbType = process.env.CI ? process.env.E2E_DB_TYPE : process.env.E2E_DEV_DB_TYPE; let dbType = process.env.CI ? process.env.E2E_DB_TYPE : process.env.E2E_DEV_DB_TYPE;
dbType = dbType || 'mysql'; dbType = dbType || 'mysql';
const response = await axios.post(`http://localhost:8080/api/v1/meta/test/reset`, { if (!process.env.CI) console.time(`setup ${process.env.TEST_PARALLEL_INDEX}`);
parallelId: process.env.TEST_PARALLEL_INDEX, let response;
dbType, try {
isEmptyProject, response = await axios.post(`http://localhost:8080/api/v1/meta/test/reset`, {
}); parallelId: process.env.TEST_PARALLEL_INDEX,
dbType,
if (response.status !== 200) { isEmptyProject,
console.error('Failed to reset test data', response.data); });
} catch (e) {
console.error(`Error resetting project: ${process.env.TEST_PARALLEL_INDEX}`, e);
}
if (!process.env.CI) console.timeEnd(`setup ${process.env.TEST_PARALLEL_INDEX}`);
if (response.status !== 200 || !response.data?.token || !response.data?.project) {
console.error('Failed to reset test data', response.data, response.status);
throw new Error('Failed to reset test data'); throw new Error('Failed to reset test data');
} }
const token = response.data.token; const token = response.data.token;

12
scripts/playwright/tests/columnFormula.spec.ts

@ -1,7 +1,7 @@
import { test } from '@playwright/test'; import { test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard'; import { DashboardPage } from '../pages/Dashboard';
import setup from '../setup'; import setup, { NcContext } from '../setup';
import { isSqlite } from '../setup/db'; import { isPg, isSqlite } from '../setup/db';
// Add formula to be verified here & store expected results for 5 rows // Add formula to be verified here & store expected results for 5 rows
// Column data from City table (Sakila DB) // Column data from City table (Sakila DB)
@ -13,7 +13,7 @@ import { isSqlite } from '../setup/db';
* Acua 2006-02-15 04:45:25 1789 Saint-Denis Parkway Mexico * Acua 2006-02-15 04:45:25 1789 Saint-Denis Parkway Mexico
* Adana 2006-02-15 04:45:25 663 Baha Blanca Parkway Turkey * Adana 2006-02-15 04:45:25 663 Baha Blanca Parkway Turkey
*/ */
const formulaData = [ const formulaDataByDbType = (context: NcContext) => [
{ {
formula: '1 + 1', formula: '1 + 1',
result: ['2', '2', '2', '2', '2'], result: ['2', '2', '2', '2', '2'],
@ -46,7 +46,9 @@ const formulaData = [
}, },
{ {
formula: `LOG({CityId}) + EXP({CityId}) + POWER({CityId}, 3) + SQRT({CountryId})`, formula: `LOG({CityId}) + EXP({CityId}) + POWER({CityId}, 3) + SQRT({CountryId})`,
result: ['13.04566088154786', '25.137588417628013', '58.23402483297667', '127.73041108667896', '284.8714548168068'], result: isPg(context)
? ['13.04566088154786', '24.74547123273205', '57.61253379902822', '126.94617671688704', '283.9609869087087']
: ['13.04566088154786', '25.137588417628013', '58.23402483297667', '127.73041108667896', '284.8714548168068'],
}, },
{ {
formula: `NOW()`, formula: `NOW()`,
@ -75,10 +77,10 @@ test.describe('Virtual Columns', () => {
test('Formula', async () => { test('Formula', async () => {
// close 'Team & Auth' tab // close 'Team & Auth' tab
const formulaData = formulaDataByDbType(context);
await dashboard.closeTab({ title: 'Team & Auth' }); await dashboard.closeTab({ title: 'Team & Auth' });
await dashboard.treeView.openTable({ title: 'City' }); await dashboard.treeView.openTable({ title: 'City' });
// Create formula column // Create formula column
await dashboard.grid.column.create({ await dashboard.grid.column.create({
title: 'NC_MATH_0', title: 'NC_MATH_0',

1
scripts/playwright/tests/columnRelationalExtendedTests.spec.ts

@ -1,6 +1,7 @@
import { test } from '@playwright/test'; import { test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard'; import { DashboardPage } from '../pages/Dashboard';
import setup from '../setup'; import setup from '../setup';
import { isPg } from '../setup/db';
test.describe('Relational Columns', () => { test.describe('Relational Columns', () => {
let dashboard: DashboardPage; let dashboard: DashboardPage;

2
scripts/playwright/tests/erd.spec.ts

@ -61,7 +61,7 @@ test.describe('Erd', () => {
await erd.dbClickShowColumnNames(); await erd.dbClickShowColumnNames();
if (isPg(context)) { if (isPg(context)) {
await erd.verifyNodesCount(mysqlSakilaTables.length); await erd.verifyNodesCount(sakilaTables.length);
await erd.verifyEdgesCount({ await erd.verifyEdgesCount({
count: 32, count: 32,
circleCount: 29, circleCount: 29,

4
scripts/playwright/tests/import.spec.ts

@ -3,7 +3,7 @@ import { airtableApiBase, airtableApiKey } from '../constants';
import { DashboardPage } from '../pages/Dashboard'; import { DashboardPage } from '../pages/Dashboard';
import { quickVerify } from '../quickTests/commonTest'; import { quickVerify } from '../quickTests/commonTest';
import setup from '../setup'; import setup from '../setup';
import { isSqlite } from '../setup/db'; import { isPg, isSqlite } from '../setup/db';
test.describe('Import', () => { test.describe('Import', () => {
let dashboard: DashboardPage; let dashboard: DashboardPage;
@ -49,7 +49,7 @@ test.describe('Import', () => {
result: expected, result: expected,
}); });
const recordCells = { Number: '1', Float: isSqlite(context) ? '1.1' : '1.10', Text: 'abc' }; const recordCells = { Number: '1', Float: isSqlite(context) || isPg(context) ? '1.1' : '1.10', Text: 'abc' };
for (const [key, value] of Object.entries(recordCells)) { for (const [key, value] of Object.entries(recordCells)) {
await dashboard.grid.cell.verify({ await dashboard.grid.cell.verify({

97
scripts/playwright/tests/metaSync.spec.ts

@ -2,7 +2,7 @@ import { test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard'; import { DashboardPage } from '../pages/Dashboard';
import { SettingsPage, SettingTab } from '../pages/Dashboard/Settings'; import { SettingsPage, SettingTab } from '../pages/Dashboard/Settings';
import setup, { NcContext } from '../setup'; import setup, { NcContext } from '../setup';
import { isSqlite, mysqlExec, sqliteExec } from '../setup/db'; import { isMysql, isPg, isSqlite, mysqlExec, pgExec, sqliteExec } from '../setup/db';
// todo: Enable when view bug is fixed // todo: Enable when view bug is fixed
test.describe('Meta sync', () => { test.describe('Meta sync', () => {
@ -23,6 +23,9 @@ test.describe('Meta sync', () => {
case 'mysql': case 'mysql':
dbExec = mysqlExec; dbExec = mysqlExec;
break; break;
case 'pg':
dbExec = pgExec;
break;
} }
}); });
@ -37,37 +40,41 @@ test.describe('Meta sync', () => {
await settings.metaData.clickReload(); await settings.metaData.clickReload();
await settings.metaData.verifyRow({ await settings.metaData.verifyRow({
index: 16, index: isPg(context) ? 21 : 16,
model: `table1`, model: `table1`,
state: 'New table', state: 'New table',
}); });
await settings.metaData.verifyRow({ await settings.metaData.verifyRow({
index: 17, index: isPg(context) ? 22 : 17,
model: `table2`, model: `table2`,
state: 'New table', state: 'New table',
}); });
await settings.metaData.sync(); await settings.metaData.sync();
await settings.metaData.verifyRow({ await settings.metaData.verifyRow({
index: 16, index: isPg(context) ? 21 : 16,
model: 'Table1', model: 'Table1',
state: 'No change identified', state: 'No change identified',
}); });
await settings.metaData.verifyRow({ await settings.metaData.verifyRow({
index: 17, index: isPg(context) ? 22 : 17,
model: 'Table2', model: 'Table2',
state: 'No change identified', state: 'No change identified',
}); });
if (!isSqlite(context)) { if (!isSqlite(context)) {
// Add relation // Add relation
await dbExec(`ALTER TABLE table1 ADD INDEX fk1_idx (col1 ASC) VISIBLE`); if (isPg(context)) {
await dbExec( await dbExec(`ALTER TABLE table1 ADD CONSTRAINT fk_idx FOREIGN KEY (id) REFERENCES table2 (id);`);
`ALTER TABLE table1 ADD CONSTRAINT fk1 FOREIGN KEY (col1) REFERENCES table2 (id) ON DELETE NO ACTION ON UPDATE NO ACTION` } else {
); await dbExec(`ALTER TABLE table1 ADD INDEX fk1_idx (col1 ASC) VISIBLE`);
await dbExec(
`ALTER TABLE table1 ADD CONSTRAINT fk1 FOREIGN KEY (col1) REFERENCES table2 (id) ON DELETE NO ACTION ON UPDATE NO ACTION`
);
}
await settings.metaData.clickReload(); await settings.metaData.clickReload();
await settings.metaData.verifyRow({ await settings.metaData.verifyRow({
index: 16, index: isPg(context) ? 21 : 16,
model: 'Table1', model: 'Table1',
state: 'New relation added', state: 'New relation added',
}); });
@ -75,17 +82,21 @@ test.describe('Meta sync', () => {
//verify after sync //verify after sync
await settings.metaData.sync(); await settings.metaData.sync();
await settings.metaData.verifyRow({ await settings.metaData.verifyRow({
index: 16, index: isPg(context) ? 21 : 16,
model: 'Table1', model: 'Table1',
state: 'No change identified', state: 'No change identified',
}); });
// Remove relation // Remove relation
await dbExec(`ALTER TABLE table1 DROP FOREIGN KEY fk1`); if (isPg(context)) {
await dbExec(`ALTER TABLE table1 DROP INDEX fk1_idx`); await dbExec(`ALTER TABLE table1 DROP CONSTRAINT fk_idx`);
} else {
await dbExec(`ALTER TABLE table1 DROP FOREIGN KEY fk1`);
await dbExec(`ALTER TABLE table1 DROP INDEX fk1_idx`);
}
await settings.metaData.clickReload(); await settings.metaData.clickReload();
await settings.metaData.verifyRow({ await settings.metaData.verifyRow({
index: 16, index: isPg(context) ? 21 : 16,
model: 'Table1', model: 'Table1',
state: 'Relation removed', state: 'Relation removed',
}); });
@ -93,21 +104,24 @@ test.describe('Meta sync', () => {
//verify after sync //verify after sync
await settings.metaData.sync(); await settings.metaData.sync();
await settings.metaData.verifyRow({ await settings.metaData.verifyRow({
index: 16, index: isPg(context) ? 21 : 16,
model: 'Table1', model: 'Table1',
state: 'No change identified', state: 'No change identified',
}); });
} }
// Add column // Add column
await dbExec( if (isSqlite(context)) {
isSqlite(context) await dbExec(`ALTER TABLE table1 ADD COLUMN newCol TEXT NULL`);
? `ALTER TABLE table1 ADD COLUMN newCol TEXT NULL` } else if (isMysql(context)) {
: `ALTER TABLE table1 ADD COLUMN newCol VARCHAR(45) NULL AFTER id` await dbExec(`ALTER TABLE table1 ADD COLUMN newCol VARCHAR(45) NULL AFTER id`);
); } else if (isPg(context)) {
await dbExec(`ALTER TABLE table1 ADD COLUMN newCol INT`);
}
await settings.metaData.clickReload(); await settings.metaData.clickReload();
await settings.metaData.verifyRow({ await settings.metaData.verifyRow({
index: 16, index: isPg(context) ? 21 : 16,
model: `Table1`, model: `Table1`,
state: 'New column(newCol)', state: 'New column(newCol)',
}); });
@ -115,20 +129,23 @@ test.describe('Meta sync', () => {
//verify after sync //verify after sync
await settings.metaData.sync(); await settings.metaData.sync();
await settings.metaData.verifyRow({ await settings.metaData.verifyRow({
index: 16, index: isPg(context) ? 21 : 16,
model: 'Table1', model: 'Table1',
state: 'No change identified', state: 'No change identified',
}); });
// Edit column // Edit column
await dbExec( if (isSqlite(context)) {
isSqlite(context) await dbExec(`ALTER TABLE table1 RENAME COLUMN newCol TO newColName`);
? `ALTER TABLE table1 RENAME COLUMN newCol TO newColName` } else if (isMysql(context)) {
: `ALTER TABLE table1 CHANGE COLUMN newCol newColName VARCHAR(45) NULL DEFAULT NULL` await dbExec(`ALTER TABLE table1 CHANGE COLUMN newCol newColName VARCHAR(45) NULL DEFAULT NULL`);
); } else if (isPg(context)) {
await dbExec(`ALTER TABLE table1 RENAME COLUMN newCol TO newColName`);
}
await settings.metaData.clickReload(); await settings.metaData.clickReload();
await settings.metaData.verifyRow({ await settings.metaData.verifyRow({
index: 16, index: isPg(context) ? 21 : 16,
model: `Table1`, model: `Table1`,
state: 'New column(newColName), Column removed(newCol)', state: 'New column(newColName), Column removed(newCol)',
}); });
@ -136,7 +153,7 @@ test.describe('Meta sync', () => {
//verify after sync //verify after sync
await settings.metaData.sync(); await settings.metaData.sync();
await settings.metaData.verifyRow({ await settings.metaData.verifyRow({
index: 16, index: isPg(context) ? 21 : 16,
model: 'Table1', model: 'Table1',
state: 'No change identified', state: 'No change identified',
}); });
@ -147,7 +164,7 @@ test.describe('Meta sync', () => {
await dbExec(`ALTER TABLE table1 DROP COLUMN newColName`); await dbExec(`ALTER TABLE table1 DROP COLUMN newColName`);
await settings.metaData.clickReload(); await settings.metaData.clickReload();
await settings.metaData.verifyRow({ await settings.metaData.verifyRow({
index: 16, index: isPg(context) ? 21 : 16,
model: `Table1`, model: `Table1`,
state: 'Column removed(newColName)', state: 'Column removed(newColName)',
}); });
@ -155,7 +172,7 @@ test.describe('Meta sync', () => {
//verify after sync //verify after sync
await settings.metaData.sync(); await settings.metaData.sync();
await settings.metaData.verifyRow({ await settings.metaData.verifyRow({
index: 16, index: isPg(context) ? 21 : 16,
model: 'Table1', model: 'Table1',
state: 'No change identified', state: 'No change identified',
}); });
@ -166,12 +183,12 @@ test.describe('Meta sync', () => {
await dbExec(`DROP TABLE table2`); await dbExec(`DROP TABLE table2`);
await settings.metaData.clickReload(); await settings.metaData.clickReload();
await settings.metaData.verifyRow({ await settings.metaData.verifyRow({
index: 16, index: isPg(context) ? 21 : 16,
model: `table1`, model: `table1`,
state: 'Table removed', state: 'Table removed',
}); });
await settings.metaData.verifyRow({ await settings.metaData.verifyRow({
index: 17, index: isPg(context) ? 22 : 17,
model: `table2`, model: `table2`,
state: 'Table removed', state: 'Table removed',
}); });
@ -190,7 +207,19 @@ test.describe('Meta sync', () => {
model: 'FilmList', model: 'FilmList',
state: 'No change identified', state: 'No change identified',
}); });
} else { }
if (isPg(context)) {
await settings.metaData.verifyRow({
index: 21,
model: 'ActorInfo',
state: 'No change identified',
});
await settings.metaData.verifyRow({
index: 22,
model: 'CustomerList',
state: 'No change identified',
});
} else if (isMysql(context)) {
await settings.metaData.verifyRow({ await settings.metaData.verifyRow({
index: 16, index: 16,
model: 'ActorInfo', model: 'ActorInfo',

13
scripts/playwright/tests/viewGridShare.spec.ts

@ -1,7 +1,7 @@
import { test } from '@playwright/test'; import { test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard'; import { DashboardPage } from '../pages/Dashboard';
import setup from '../setup'; import setup from '../setup';
import { isSqlite } from '../setup/db'; import { isMysql, isPg, isSqlite } from '../setup/db';
test.describe('Shared view', () => { test.describe('Shared view', () => {
let dashboard: DashboardPage; let dashboard: DashboardPage;
@ -77,13 +77,14 @@ test.describe('Shared view', () => {
await sharedPage.grid.column.verify(column); await sharedPage.grid.column.verify(column);
} }
const expectedRecordsByDb = isSqlite(context) ? sqliteExpectedRecords : expectedRecords; const expectedRecordsByDb = isSqlite(context) || isPg(context) ? sqliteExpectedRecords : expectedRecords;
// verify order of records (original sort & filter) // verify order of records (original sort & filter)
for (const record of expectedRecordsByDb) { for (const record of expectedRecordsByDb) {
await sharedPage.grid.cell.verify(record); await sharedPage.grid.cell.verify(record);
} }
const expectedVirtualRecordsByDb = isSqlite(context) ? sqliteExpectedVirtualRecords : expectedVirtualRecords; const expectedVirtualRecordsByDb =
isSqlite(context) || isPg(context) ? sqliteExpectedVirtualRecords : expectedVirtualRecords;
// verify virtual records // verify virtual records
for (const record of expectedVirtualRecordsByDb) { for (const record of expectedVirtualRecordsByDb) {
@ -104,7 +105,7 @@ test.describe('Shared view', () => {
isLocallySaved: true, isLocallySaved: true,
}); });
if (!isSqlite(context)) { if (isMysql(context)) {
await sharedPage.grid.toolbar.filter.addNew({ await sharedPage.grid.toolbar.filter.addNew({
columnTitle: 'District', columnTitle: 'District',
value: 'Ta', value: 'Ta',
@ -120,7 +121,7 @@ test.describe('Shared view', () => {
await sharedPage.grid.column.verify(column); await sharedPage.grid.column.verify(column);
} }
const expectedRecordsByDb2 = isSqlite(context) ? sqliteExpectedRecords2 : expectedRecords2; const expectedRecordsByDb2 = isSqlite(context) || isPg(context) ? sqliteExpectedRecords2 : expectedRecords2;
// verify order of records (original sort & filter) // verify order of records (original sort & filter)
for (const record of expectedRecordsByDb2) { for (const record of expectedRecordsByDb2) {
await sharedPage.grid.cell.verify(record); await sharedPage.grid.cell.verify(record);
@ -134,7 +135,7 @@ test.describe('Shared view', () => {
// verify download // verify download
await sharedPage.grid.toolbar.clickDownload( await sharedPage.grid.toolbar.clickDownload(
'Download as CSV', 'Download as CSV',
isSqlite(context) ? 'expectedDataSqlite.txt' : 'expectedData.txt' isSqlite(context) || isPg(context) ? 'expectedDataSqlite.txt' : 'expectedData.txt'
); );
}); });

10
scripts/playwright/tests/viewKanban.spec.ts

@ -3,7 +3,7 @@ import { DashboardPage } from '../pages/Dashboard';
import { ToolbarPage } from '../pages/Dashboard/common/Toolbar'; import { ToolbarPage } from '../pages/Dashboard/common/Toolbar';
import setup from '../setup'; import setup from '../setup';
import { isSqlite } from '../setup/db'; import { isPg, isSqlite } from '../setup/db';
const filmRatings = ['G', 'PG', 'PG-13', 'R', 'NC-17']; const filmRatings = ['G', 'PG', 'PG-13', 'R', 'NC-17'];
@ -20,7 +20,13 @@ test.describe('View', () => {
await dashboard.closeTab({ title: 'Team & Auth' }); await dashboard.closeTab({ title: 'Team & Auth' });
await dashboard.treeView.openTable({ title: 'Film' }); await dashboard.treeView.openTable({ title: 'Film' });
if (isSqlite(context)) { if (isPg(context)) {
// Since these view depend on the Ratings column of the Film table
await dashboard.treeView.deleteTable({ title: 'NicerButSlowerFilmList' });
await dashboard.treeView.deleteTable({ title: 'FilmList' });
}
if (isSqlite(context) || isPg(context)) {
await dashboard.grid.column.openEdit({ title: 'Rating' }); await dashboard.grid.column.openEdit({ title: 'Rating' });
await dashboard.grid.column.selectType({ type: 'SingleSelect' }); await dashboard.grid.column.selectType({ type: 'SingleSelect' });
let count = 0; let count = 0;

4
scripts/playwright/tests/viewMenu.spec.ts

@ -1,6 +1,7 @@
import { test } from '@playwright/test'; import { test } from '@playwright/test';
import { DashboardPage } from '../pages/Dashboard'; import { DashboardPage } from '../pages/Dashboard';
import setup from '../setup'; import setup from '../setup';
import { isPg } from '../setup/db';
test.describe('Grid view locked', () => { test.describe('Grid view locked', () => {
let dashboard: DashboardPage; let dashboard: DashboardPage;
@ -44,6 +45,9 @@ test.describe('Grid view locked', () => {
await dashboard.grid.toolbar.viewsMenu.click({ await dashboard.grid.toolbar.viewsMenu.click({
menu: 'Download', menu: 'Download',
subMenu: 'Download as CSV', subMenu: 'Download as CSV',
verificationInfo: {
verificationFile: isPg(context) ? './fixtures/expectedBaseDownloadDataPg.txt' : null,
},
}); });
}); });
}); });

Loading…
Cancel
Save