console: e2e test for graphql customization while adding data source

Use this docker-compose file while running the connect DB cypress test

<details>
<summary>Docker-compose file</summary>

```yaml
version: '3.6'
services:
  postgres:
    image: postgres:12
    restart: unless-stopped
    ports:
      - '5432:5432'
    volumes:
      - db_meta:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: postgrespassword
  postgres12:
    image: postgres:12
    restart: unless-stopped
    ports:
      - '4432:5432'
    volumes:
      - db_pg12_data:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: postgrespassword
  msserver:
    image: 'mcr.microsoft.com/mssql/server:2019-latest'
    ports:
      - '1435:1435'
    restart: unless-stopped
    environment:
      SA_PASSWORD: 'testPassword123'
      ACCEPT_EULA: 'Y'
  graphql-engine:
    image: hasura/graphql-engine:v2.8.1
    ports:
      - '8080:8080'
    depends_on:
      - 'postgres'
    restart: unless-stopped
    environment:
      HASURA_GRAPHQL_DATABASE_URL: postgres://postgres:postgrespassword@postgres:5432/postgres
      TEST_MSSQL_DATABASE_URL: DRIVER={ODBC Driver 17 for SQL Server};SERVER=msserver;Uid=SA;Pwd=testPassword123
      ## enable the console served by server
      HASURA_GRAPHQL_ENABLE_CONSOLE: 'true' # set to "false" to disable console
      ## enable debugging mode. It is recommended to disable this in production
      ## HASURA_GRAPHQL_CONSOLE_ASSETS_DIR: /srv/console-assets
      HASURA_GRAPHQL_DEV_MODE: 'true'
      HASURA_GRAPHQL_ENABLED_LOG_TYPES: startup, http-log, webhook-log, websocket-log, query-log
      ## uncomment next line to set an admin secret
      # HASURA_GRAPHQL_ADMIN_SECRET: myadminsecretkey
      HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS: 'true'
      HASURA_GRAPHQL_EXPERIMENTAL_FEATURES: naming_convention
volumes:
  db_pg12_data:
  db_meta:
# Connect to second PG using env var DB2
# Connect to metadata PG using HASURA_GRAPHQL_METADATA_DATABASE_URL
# Connect to mssql server using connection string (not env)
# DRIVER={ODBC Driver 17 for SQL Server};SERVER=msserver;Uid=SA;Pwd=testPassword123
# PG 12 : postgres://postgres:postgrespassword@postgres12:5432/postgres
```

</details>

1. Here is the [Video](https://www.loom.com/share/bfe8b4da59ae48dca3fc9877b440c360) of the running connect db e2e test
2. We write test only for connect Postgres DB not for mssql because mssql container doesn't work on CI and on M1 laptops. Moreover, the value provided was low because we tested the same path Postgres (this was team agreement)
3. We installed @testing-library/cypress to leverage the same helper functions (we used `findByText` and `findByPlaceholderText`)
4. We used `withIn` to target a section on a page and find an element inside
5. We used a procedural approach and we added `cy.log` to improve the debugging of the test.
6. We created new helper function to test notifications -

`cy.expectSuccessNotification()`
`cy.expectSuccessNotificationWithTitle(title: string)`
`cy.expectSuccessNotificationWithMessage(message: string)`
`cy.expectErrorNotification()`
`cy.expectErrorNotificationWithTitle(title: string)`
`cy.expectErrorNotificationWithMessage(message: string)`

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/4960
Co-authored-by: Luca Restagno <59067245+lucarestagno@users.noreply.github.com>
Co-authored-by: Brandon Simmons <210815+jberryman@users.noreply.github.com>
Co-authored-by: Samir Talwar <47582+SamirTalwar@users.noreply.github.com>
Co-authored-by: Divi <32202683+imperfect-fourth@users.noreply.github.com>
Co-authored-by: Karthikeyan Chinnakonda <15602904+codingkarthik@users.noreply.github.com>
Co-authored-by: Erik Magnusson <32518962+ejkkan@users.noreply.github.com>
Co-authored-by: Vishnu Bharathi <4211715+scriptnull@users.noreply.github.com>
Co-authored-by: Jesse Hallett <9622+hallettj@users.noreply.github.com>
Co-authored-by: Abby Sassel <3883855+sassela@users.noreply.github.com>
Co-authored-by: David Overton <7734777+dmoverton@users.noreply.github.com>
Co-authored-by: Daniel Chambers <1214352+daniel-chambers@users.noreply.github.com>
Co-authored-by: Lyndon Maydwell <92299+sordina@users.noreply.github.com>
Co-authored-by: paritosh-08 <85472423+paritosh-08@users.noreply.github.com>
Co-authored-by: Gil Mizrahi <8547573+soupi@users.noreply.github.com>
Co-authored-by: Benoit Ranque <25712958+BenoitRanque@users.noreply.github.com>
Co-authored-by: Daniel Harvey <4729125+danieljharvey@users.noreply.github.com>
Co-authored-by: Tom Harding <6302310+i-am-tom@users.noreply.github.com>
Co-authored-by: Solomon <24038+solomon-b@users.noreply.github.com>
Co-authored-by: Antoine Leblanc <1618949+nicuveo@users.noreply.github.com>
Co-authored-by: Evie Ciobanu <1017953+eviefp@users.noreply.github.com>
Co-authored-by: Vijay Prasanna <11921040+vijayprasanna13@users.noreply.github.com>
Co-authored-by: Naveen Naidu <30195193+Naveenaidu@users.noreply.github.com>
Co-authored-by: Abhijeet Khangarot <26903230+abhi40308@users.noreply.github.com>
Co-authored-by: Rob Dominguez <24390149+robertjdominguez@users.noreply.github.com>
Co-authored-by: Puru Gupta <32328846+purugupta99@users.noreply.github.com>
Co-authored-by: Daniele Cammareri <5709409+dancamma@users.noreply.github.com>
Co-authored-by: Marion Schleifer <5722022+marionschleifer@users.noreply.github.com>
GitOrigin-RevId: a50879ee790d3409c0b97c87a0e3d2b793c9ff37
This commit is contained in:
Varun Choudhary 2022-07-21 15:00:18 +05:30 committed by hasura-bot
parent 6821b90910
commit 69d4d8c6df
18 changed files with 1108 additions and 504 deletions

View File

@ -99,9 +99,10 @@ export const testSessVariable = () => {
.clear()
.type('invalid');
cy.get(getElementFromAlias(`${fN}-session-argument-save`)).click();
cy.get('.notification-error', { timeout: 5000 })
.should('be.visible')
.and('contain', 'Updating Session argument variable failed');
cy.expectErrorNotificationWithTitle(
'Updating Session argument variable failed'
);
cy.get(getElementFromAlias(`${fN}-session-argument-btn`), {
timeout: 1000,

View File

@ -366,7 +366,6 @@ export const failBIUniqueKeys = () => {
cy.get(getElementFromAlias(`typed-input-${textIndex}`)).type('name');
cy.get(getElementFromAlias('insert-save-button')).click();
// cy.get('.notification-error').click();
cy.wait(7000);
validateInsert(getTableName(0, testName), 21);
};

View File

@ -1,88 +1,52 @@
import { setPromptWithCb } from '../../../helpers/common';
import { getIndexRoute, baseUrl } from '../../../helpers/dataHelpers';
import { setMetaData } from '../../validators/validators';
export const navigateAndOpenConnectDatabasesForm = () => {
// visit index route and set metadata
cy.visit(getIndexRoute()).then(setMetaData);
cy.location('pathname').then(currentPage => {
const alreadyOnThePage = currentPage.startsWith('/data/manage');
if (!alreadyOnThePage) {
cy.log('**--- Navigate to Connect Databases Form**');
// visit the manage database route
cy.getBySel('sidebar-manage-database').click();
cy.url().should('eq', `${baseUrl}/data/manage`);
cy.log('**--- visit index route and set metadata**');
cy.visit('/data/default/schema/public').then(setMetaData);
// open the create database form
cy.get('button').contains('Connect Database').click();
cy.url().should('eq', `${baseUrl}/data/manage/connect`);
cy.log('**--- Click on the manage database menu**');
cy.findByRole('button', { name: 'Manage' }).click();
cy.location('pathname').should('eq', '/data/manage');
}
});
// open up the connection settings section of the form so that all <input> elements are visible
cy.get('a').contains('Connection Settings').click();
};
cy.log('**--- Click on the Connect Database button**');
cy.findByRole('button', { name: 'Connect Database' }).click();
cy.location('pathname').should('eq', '/data/manage/connect');
export const expectNotif = (
type: string,
{
title,
message,
}: {
title: string;
message?: string;
},
timeout = 10000
) => {
const types: Record<string, string> = {
error: '.notification-error',
success: '.notification-success',
};
const el = cy.get(types[type], { timeout });
el.should('be.visible');
el.should('contain', title);
if (message) el.should('contain', message);
cy.get('form').within(() => {
cy.log('**--- Click on Connection Settings section**');
cy.contains('Connection Settings').click();
});
cy.get('form').within(() => {
cy.log('**--- Click on GraphQL Field Customization section**');
cy.contains('GraphQL Field Customization').click();
});
};
export const navigateToManageDatabases = () => {
// visit index route and set metadata
cy.visit(getIndexRoute()).then(setMetaData);
cy.log('**--- Visit index route and set metadata**');
cy.visit('/data/default/schema/public').then(setMetaData);
// visit the manage database route
cy.log('**--- Visit the manage database route**');
cy.getBySel('sidebar-manage-database').click();
cy.url().should('eq', `${baseUrl}/data/manage`);
cy.location('pathname').should('eq', '/data/manage');
};
export const submitConnectDBForm = () => {
cy.log('**--- Click on the Connect Database button**');
cy.getBySel('connect-database-btn').click();
};
export const removeDBFromList = (dbName: string) => {
setPromptWithCb(dbName, () => {
cy.log('**--- Click on the Remove Database button**');
cy.getBySel(dbName).find('button').contains('Remove').click();
});
};
export const verifyUrl = (url: string) => {
cy.url().should('eq', url);
};
export type driverSpecType = {
name: 'postgres' | 'mssql';
tests: {
addDBWithURL?: (dbName: string) => void;
addDBWithConnParams?: (dbName: string) => void;
addDBWithEnvVar?: (dbName: string) => void;
removeDBFromList?: (dbName: string) => void;
addDBWithURLAndExpectDuplicateError?: (dbName: string) => void;
fillDetailsForEnvVarForm?: (dbName: string) => void;
fillDetailsForDbUrlForm?: (dbName: string) => void;
fillDetailsForConnParamsForm?: (dbName: string) => void;
};
helpers: {
createDB: (dbName: string) => void;
removeDB: (dbName: string) => void;
createTable: (tableName: string) => void;
deleteTable: (tableName: string) => void;
trackTable: (tableName: string) => void;
untrackTable: (tableName: string) => void;
createRemoteSchema: (remoteSchemaName: string) => void;
deleteRemoteSchema: (remoteSchemaName: string) => void;
};
};

View File

@ -0,0 +1,156 @@
import { baseUrl, testMode } from '../../../helpers/common';
import {
navigateAndOpenConnectDatabasesForm,
navigateToManageDatabases,
removeDBFromList,
submitConnectDBForm,
} from './common.spec';
import {
createDB,
fillDetailsForPgConnParamsForm,
fillDetailsForPgDbUrlForm,
fillDetailsForPgEnvVarForm,
removeDB,
} from './postgres.spec';
const connectPgDatabaseFormTests = () => {
describe('Add a database via connect form', () => {
describe('can successfully add', () => {
describe('a postgres database', () => {
it('using a connection string', () => {
cy.log('**------------------------------**');
cy.log('**--- Add postgres DB via connection string**');
cy.log('**------------------------------**');
navigateAndOpenConnectDatabasesForm();
fillDetailsForPgDbUrlForm('postgres_db_with_url');
submitConnectDBForm();
cy.log('**--- Notifies that DB is being added');
cy.expectSuccessNotificationWithTitle('Adding data source...');
cy.log('**--- Redirects to Data Manager page');
cy.getBySel('manage-database-section').within(() => {
cy.findByText('Data Manager');
});
cy.url().should('eq', `${baseUrl}/data/manage`);
cy.log('**--- has postgres_db_with_url on manage page');
cy.findByText('postgres_db_with_url');
cy.log('**--- has success notification displayed');
cy.expectSuccessNotificationWithTitle(
'Data source added successfully!'
);
cy.log('**--- Remove database**');
removeDB('postgres_db_with_url');
});
it('using connection parameters', () => {
cy.log('**------------------------------**');
cy.log('**--- Add postgres DB via connection parameters**');
cy.log('**------------------------------**');
navigateAndOpenConnectDatabasesForm();
fillDetailsForPgConnParamsForm('postgres_db_with_conn_param');
submitConnectDBForm();
cy.log('**--- notifies that db is being added');
cy.expectSuccessNotificationWithTitle('Adding data source...');
cy.log('**--- redirects to Data Manager page');
cy.getBySel('manage-database-section').within(() => {
cy.findByText('Data Manager');
});
cy.url().should('eq', `${baseUrl}/data/manage`);
cy.log('**--- has postgres_db_with_conn_param on manage page');
cy.findByText('postgres_db_with_conn_param');
cy.log('**--- has success notification displayed');
cy.expectSuccessNotificationWithTitle(
'Data source added successfully!'
);
cy.log('**--- Remove database**');
removeDB('postgres_db_with_conn_param');
});
it('using environment variables', () => {
cy.log('**------------------------------**');
cy.log('**--- Add postgres DB via env vars**');
cy.log('**------------------------------**');
navigateAndOpenConnectDatabasesForm();
fillDetailsForPgEnvVarForm('postgres_db_with_env_var');
submitConnectDBForm();
cy.log('**--- notifies that db is being added');
cy.expectSuccessNotificationWithTitle('Adding data source...');
cy.log('**--- redirects to Data Manager page');
cy.getBySel('manage-database-section').within(() => {
cy.findByText('Data Manager');
});
cy.url().should('eq', `${baseUrl}/data/manage`);
cy.log('**--- has postgres_db_with_env_var on manage page');
cy.findByText('postgres_db_with_env_var');
cy.log('**--- has success notification displayed');
cy.expectSuccessNotificationWithTitle(
'Data source added successfully!'
);
cy.log('**--- Remove database**');
removeDB('postgres_db_with_env_var');
});
});
});
describe('fails on submitting', () => {
describe('an empty form', () => {
it('submit with no inputs filled in', () => {
navigateAndOpenConnectDatabasesForm();
cy.getBySel('connect-database-btn').click();
cy.expectErrorNotification();
});
});
it('with a duplicate database name', () => {
navigateAndOpenConnectDatabasesForm();
createDB('test');
fillDetailsForPgDbUrlForm('test');
submitConnectDBForm();
cy.log('**--- verify error notification');
cy.expectErrorNotificationWithTitle('Adding data source failed');
cy.log('**--- Remove database**');
removeDB('test');
});
});
});
};
const manageDatabasesPageTests = () => {
describe('Connected Databases list page', () => {
it('can successfully remove db', () => {
cy.log('**--- Create database**');
createDB('db_for_removal');
navigateToManageDatabases();
cy.log('**--- use the remove button');
removeDBFromList('db_for_removal');
cy.log('**--- verify success notification');
cy.expectSuccessNotificationWithTitle('Data source removed successfully');
});
});
};
if (testMode !== 'cli') {
connectPgDatabaseFormTests();
manageDatabasesPageTests();
}

View File

@ -1,84 +0,0 @@
import { driverSpecType } from './common.spec';
// Use this config for running locally
// const config = {
// host: '172.17.0.1',
// port: '1433',
// dbName: 'master',
// username: 'SA',
// password: 'reallyStrongPwd123',
// };
const config = {
host: 'localhost',
port: '1433',
dbName: 'master',
username: 'SA',
password: 'hasuraMSSQL1',
};
const dbUrl = `DRIVER={ODBC Driver 17 for SQL Server};SERVER=${config.host};DATABASE=${config.dbName};Uid=${config.username};Pwd=${config.password}`;
const fillDetailsForDbUrlForm = (dbName: string) => {
cy.getBySel('database-display-name').type(dbName);
cy.getBySel('database-type').select('mssql');
cy.getBySel('database-url').type(dbUrl, { parseSpecialCharSequences: false });
cy.getBySel('max-connections').type('10');
cy.getBySel('idle-timeout').type('100');
};
const fillDetailsForEnvVarForm = (dbName: string) => {
cy.getBySel('database-display-name').type(dbName);
cy.getBySel('database-type').select('mssql');
cy.get("input[type='radio']").eq(0).click();
cy.getBySel('database-url-env').type('TEST_MSSQL_DATABASE_URL');
};
export const removeDB = (dbName: string) => {
const postBody = { type: 'mssql_drop_source', args: { name: dbName } };
cy.request('POST', 'http://localhost:8080/v1/metadata', postBody).then(
response => {
expect(response.body).to.have.property('message', 'success'); // true
}
);
cy.reload();
};
export const createDB = (dbName: string) => {
const postBody = {
type: 'mssql_add_source',
args: {
name: dbName,
configuration: {
connection_info: {
database_url: dbUrl,
},
},
},
};
cy.request('POST', 'http://localhost:8080/v1/metadata', postBody).then(
response => {
expect(response.body).to.have.property('message', 'success'); // true
}
);
};
const mssql: driverSpecType = {
name: 'mssql',
tests: {
fillDetailsForDbUrlForm,
fillDetailsForEnvVarForm,
},
helpers: {
removeDB,
createDB,
createTable: () => {},
createRemoteSchema: () => {},
deleteRemoteSchema: () => {},
deleteTable: () => {},
trackTable: () => {},
untrackTable: () => {},
},
};
export { mssql };

View File

@ -1,54 +1,69 @@
import { driverSpecType } from './common.spec';
// Use this config for running locally
// const config = {
// host: 'postgres',
// port: '5432',
// dbName: 'postgres',
// username: 'postgres',
// password: 'postgrespassword',
// };
const config = {
host: 'localhost',
host: 'postgres',
port: '5432',
dbName: 'gql_test',
username: 'gql_test',
password: '',
dbName: 'postgres',
username: 'postgres',
password: 'postgrespassword',
};
const dbUrl = `postgres://${config.username}:${config.password}@${config.host}:${config.port}/${config.dbName}`;
const fillDetailsForDbUrlForm = (dbName: string) => {
const graphqlCustomizationTest = () => {
cy.log('**--- Select a graphql naming convention**');
cy.get('[data-test="GraphQL Field Customization"]').within(() => {
cy.get('[data-test="radio-select-hasura-default"]').should('be.visible');
cy.get('[data-test="radio-select-graphql-default"]').should('be.visible');
cy.get('label[for="radio-select-graphql-default"]').click();
});
cy.log('**--- Fill Root Fields Customizations**');
cy.get('form[aria-label="rootFields"]').within(() => {
cy.findByPlaceholderText('Namespace...').type('name_space');
cy.findByPlaceholderText('prefix_').type('prefix_');
cy.findByPlaceholderText('_suffix').type('_suffix');
});
cy.log('**--- Fill Type Names Customizations**');
cy.get('form[aria-label="typeNames"]').within(() => {
cy.findByPlaceholderText('prefix_').type('prefix_');
cy.findByPlaceholderText('_suffix').type('_suffix');
});
};
export const fillDetailsForPgDbUrlForm = (dbName: string) => {
cy.log('**--- Fill Form using db url**');
cy.getBySel('database-display-name').type(dbName);
cy.getBySel('database-type').select('postgres');
cy.getBySel('database-url').type(dbUrl);
cy.getBySel('max-connections').type('50');
cy.getBySel('idle-timeout').type('180');
cy.getBySel('retries').type('1');
graphqlCustomizationTest();
};
const fillDetailsForConnParamsForm = (dbName: string) => {
export const fillDetailsForPgConnParamsForm = (dbName: string) => {
cy.log('**--- Fill Form using connection parameter**');
cy.get("input[type='radio']").eq(2).click();
cy.getBySel('database-display-name').type(dbName);
cy.getBySel('database-type').select('postgres');
cy.getBySel('host').type(config.host);
cy.getBySel('port').type(config.port);
cy.getBySel('username').type(config.username);
if (config.password) {
cy.getBySel('password').type(config.password);
}
cy.getBySel('password').type(config.password);
cy.getBySel('database-name').type(config.dbName);
graphqlCustomizationTest();
};
const fillDetailsForEnvVarForm = (dbName: string) => {
export const fillDetailsForPgEnvVarForm = (dbName: string) => {
cy.log('**--- Fill Form using env vars**');
cy.get("input[type='radio']").eq(0).click();
cy.getBySel('database-display-name').type(dbName);
cy.getBySel('database-type').select('postgres');
cy.getBySel('database-url-env').type('HASURA_GRAPHQL_DATABASE_URL');
graphqlCustomizationTest();
};
const createDB = (dbName: string) => {
export const createDB = (dbName: string) => {
const postBody = {
type: 'pg_add_source',
args: {
@ -67,7 +82,7 @@ const createDB = (dbName: string) => {
);
};
const removeDB = (dbName: string) => {
export const removeDB = (dbName: string) => {
const postBody = { type: 'pg_drop_source', args: { name: dbName } };
cy.request('POST', 'http://localhost:8080/v1/metadata', postBody).then(
response => {
@ -181,24 +196,3 @@ const deleteRemoteSchema = (remoteSchemaName: string) => {
}
);
};
const postgres: driverSpecType = {
name: 'postgres',
tests: {
fillDetailsForDbUrlForm,
fillDetailsForConnParamsForm,
fillDetailsForEnvVarForm,
},
helpers: {
createDB,
removeDB,
createTable,
createRemoteSchema,
deleteRemoteSchema,
deleteTable,
trackTable,
untrackTable,
},
};
export { postgres };

View File

@ -1,145 +0,0 @@
import { baseUrl, testMode } from '../../../helpers/common';
import {
driverSpecType,
expectNotif,
navigateAndOpenConnectDatabasesForm,
navigateToManageDatabases,
removeDBFromList,
submitConnectDBForm,
verifyUrl,
} from './common.spec';
import { postgres } from './postgres.spec';
import { mssql } from './mssql.spec';
const connectDatabaseFormTests = () => {
describe('Adding a database via connect form', () => {
/*
Check if the connect DB form is able to create different types of databases
*/
const drivers: driverSpecType[] = [postgres, mssql];
describe('can successfully add', () => {
drivers.forEach(driver => {
describe(`a ${driver.name} database`, () => {
const connectOptions: Record<string, any> = {
with_url: {
test: driver.tests.fillDetailsForDbUrlForm,
description: 'using a connection string',
},
with_conn_param: {
test: driver.tests.fillDetailsForConnParamsForm,
description: 'using connection parameters',
},
with_env_var: {
test: driver.tests.fillDetailsForEnvVarForm,
description: 'using a ENV variable',
},
};
Object.keys(connectOptions).forEach(key => {
const option = connectOptions[key].test;
if (option) {
describe(connectOptions[key].description, () => {
before(() => {
navigateAndOpenConnectDatabasesForm();
option(`${driver.name}_db_${key}`);
submitConnectDBForm();
});
it('notifies that db is being added', () => {
expectNotif('success', {
title: 'Adding data source...',
});
});
it('redirects to Data Manager page', () => {
cy.contains('Data Manager');
verifyUrl(`${baseUrl}/data/manage`);
});
it(`has ${driver.name}_db_${key} on manage page`, () => {
cy.contains(`${driver.name}_db_${key}`);
});
it('has success notification displayed', () => {
expectNotif('success', {
title: 'Data source added successfully!',
});
});
// cleanup
after(() => {
driver.helpers.removeDB(`${driver.name}_db_${key}`);
});
});
}
});
});
});
});
/*
Check if the connect DB form's submit action results in an error for the following cases -
1. an empty from
2. with a duplicate database name
*/
describe('fails on submitting', () => {
describe('an empty from', () => {
it('submit with no inputs filled in', () => {
navigateAndOpenConnectDatabasesForm();
cy.getBySel('connect-database-btn').click();
cy.get('.notification-error').should('be.visible');
});
});
describe('with a duplicate database name', () => {
// Add a db named "test" using the postgres helper and use the connect form to create a db with the same name "test"
before(() => {
navigateAndOpenConnectDatabasesForm();
postgres.helpers.createDB('test');
if (postgres.tests.fillDetailsForDbUrlForm)
postgres.tests.fillDetailsForDbUrlForm('test');
submitConnectDBForm();
});
// it should return an error notification
it('verify error notification', () => {
expectNotif('error', {
title: 'Adding data source failed',
});
});
// cleanup
after(() => {
postgres.helpers.removeDB('test');
});
});
});
});
};
const manageDatabasesPageTests = () => {
describe('Connected Databases list page', () => {
describe('can successfully remove db', () => {
before(() => {
postgres.helpers.createDB('db_for_removal');
navigateToManageDatabases();
});
it('use the remove button', () => {
removeDBFromList('db_for_removal');
});
it('verify success notification', () => {
expectNotif('success', {
title: 'Data source removed successfully',
});
});
});
});
};
if (testMode !== 'cli') {
connectDatabaseFormTests();
manageDatabasesPageTests();
}

View File

@ -80,7 +80,6 @@ export const passTrackTable = () => {
getElementFromAlias('add-track-table-author_average_rating_vt')
).click();
cy.wait(7000);
// cy.get('.notification-error');
validateView('author_average_rating_vt', ResultType.SUCCESS);
};
@ -401,7 +400,6 @@ export const passVDeleteMaterializedView = () => {
cy.get(getElementFromAlias('delete-view')).click();
cy.window().its('prompt').should('be.called');
cy.wait(7000);
// cy.get('.notification-error');
validateView('author_average_rating_vt', ResultType.FAILURE);
};

View File

@ -1,7 +1,6 @@
/* eslint no-unused-vars: 0 */
/* eslint import/prefer-default-export: 0 */
import { testMode } from '../../../helpers/common';
import { expectNotif } from '../../data/manage-database/common.spec';
import { setMetaData } from '../../validators/validators';
import { modifyCustomization } from './spec';
@ -87,9 +86,7 @@ const editSchemaTests = () => {
});
it('expect success notification', () => {
expectNotif('success', {
title: 'Remote schema modified',
});
cy.expectSuccessNotificationWithTitle('Remote schema modified');
});
});

View File

@ -34,9 +34,12 @@ export const duplicateAndEmptyDomainError = () => {
cy.get(getElementFromAlias('add-tls-allow-list')).click();
cy.wait(1000);
// duplicate error
cy.get('.notification-error', { timeout: 5000 })
.should('be.visible')
.and('contain', 'Adding domain to insecure TLS allow list failed');
// NOTE: is a timeout 5000 really needed?
cy.expectErrorNotificationWithTitle(
'Adding domain to insecure TLS allow list failed'
);
// close add domain dialog box
cy.get(getElementFromAlias('cancel-domain')).click();
// click add domain
@ -44,9 +47,8 @@ export const duplicateAndEmptyDomainError = () => {
// click Add to Allow List with empty domain name
cy.get(getElementFromAlias('add-tls-allow-list')).click();
// empty domain error
cy.get('.notification-error', { timeout: 5000 })
.should('be.visible')
.and('contain', 'No domain found');
// NOTE: is a timeout 5000 really needed?
cy.expectErrorNotificationWithTitle('No domain found');
cy.get(getElementFromAlias('cancel-domain')).click();
};

View File

@ -25,9 +25,11 @@
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
import 'cypress-wait-until';
import '@testing-library/cypress/add-commands';
import './visitEmptyPage';
import './clearConsoleTextarea';
import './notifications'
Cypress.Commands.add('getBySel', (selector, ...args) => {
return cy.get(`[data-test=${selector}]`, ...args);

View File

@ -20,5 +20,11 @@ declare namespace Cypress {
* @example cy.visitEmptyPage()
*/
visitEmptyPage(): Chainable<unknown>;
expectSuccessNotification(): Chainable<unknown>;
expectSuccessNotificationWithTitle(title: string): Chainable<unknown>;
expectSuccessNotificationWithMessage(message: string): Chainable<unknown>;
expectErrorNotification(): Chainable<unknown>;
expectErrorNotificationWithTitle(title: string): Chainable<unknown>;
expectErrorNotificationWithMessage(message: string): Chainable<unknown>;
}
}

View File

@ -0,0 +1,39 @@
Cypress.Commands.add('expectSuccessNotification', () => {
const el = cy.get('.notification-success');
el.should('be.visible');
});
Cypress.Commands.add('expectSuccessNotificationWithTitle', (title: string) => {
const el = cy.get('.notification-success');
el.should('be.visible');
el.should('contain', title);
});
Cypress.Commands.add(
'expectSuccessNotificationWithMessage',
(message: string) => {
const el = cy.get('.notification-success');
el.should('be.visible');
el.should('contain', message);
}
);
Cypress.Commands.add('expectErrorNotification', () => {
const el = cy.get('.notification-error');
el.should('be.visible');
});
Cypress.Commands.add('expectErrorNotificationWithTitle', (title: string) => {
const el = cy.get('.notification-error');
el.should('be.visible');
el.should('contain', title);
});
Cypress.Commands.add(
'expectErrorNotificationWithMessage',
(message: string) => {
const el = cy.get('.notification-error');
el.should('be.visible');
el.should('contain', message);
}
);

View File

@ -6,7 +6,7 @@
"baseUrl": "../node_modules",
"target": "es5",
"lib": ["es2015", "dom"],
"types": ["cypress", "cypress-wait-until"]
"types": ["cypress", "cypress-wait-until", "@testing-library/cypress"]
},
"include": ["**/*.ts"]
}

File diff suppressed because it is too large Load Diff

View File

@ -163,6 +163,7 @@
"@storybook/testing-react": "1.2.3",
"@tailwindcss/forms": "^0.3.3",
"@tailwindcss/typography": "^0.4.1",
"@testing-library/cypress": "^8.0.3",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^11.2.7",
"@testing-library/react-hooks": "7.0.2",
@ -230,7 +231,7 @@
"concurrently": "5.2.0",
"cross-env": "7.0.2",
"css-loader": "3.5.3",
"cypress": "^10.2.0",
"cypress": "^10.3.0",
"cypress-wait-until": "^1.7.2",
"dedent": "0.7.0",
"dotenv": "5.0.1",

View File

@ -266,7 +266,10 @@ const ManageDatabase: React.FC<ManageDatabaseProps> = ({
return (
<RightContainer>
<Helmet title="Manage - Data | Hasura" />
<div className={`container-fluid ${styles.manage_dbs_page}`}>
<div
className={`container-fluid ${styles.manage_dbs_page}`}
data-test="manage-database-section"
>
<BreadCrumb breadCrumbs={crumbs} />
<div className={styles.padd_top}>
<div className={`${styles.display_flex} manage-db-header`}>

View File

@ -110,7 +110,7 @@ export const Collapse = ({
};
return (
<Collapsible.Root open={open} onOpenChange={onOpenChange}>
<Collapsible.Root open={open} onOpenChange={onOpenChange} data-test={title}>
<CollapseCtx.Provider value={{ open }}>
{!!title && (
<CollapseHeader