mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-09-21 07:28:26 +03:00
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:
parent
6821b90910
commit
69d4d8c6df
@ -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,
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
@ -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();
|
||||
}
|
@ -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 };
|
@ -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('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 };
|
||||
|
@ -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();
|
||||
}
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -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();
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
6
console/cypress/support/index.d.ts
vendored
6
console/cypress/support/index.d.ts
vendored
@ -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>;
|
||||
}
|
||||
}
|
||||
|
39
console/cypress/support/notifications.ts
Normal file
39
console/cypress/support/notifications.ts
Normal 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);
|
||||
}
|
||||
);
|
@ -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"]
|
||||
}
|
||||
|
965
console/package-lock.json
generated
965
console/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -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",
|
||||
|
@ -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`}>
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user