console: improve actions E2E tests with rest connectors

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/4520
GitOrigin-RevId: 24d9176dc5f93747e4b3a12ed08171064e75900e
This commit is contained in:
Stefano Magni 2022-06-28 07:39:33 +02:00 committed by hasura-bot
parent ab8369bdcf
commit fb1dc2e0ba
12 changed files with 319 additions and 583 deletions

View File

@ -6,12 +6,6 @@ export const togglePayloadTransformSection = () => {
});
};
export const toggleContextArea = () => {
cy.getBySel('toggle-context-area').click({
force: true,
});
};
export const toggleRequestTransformSection = () => {
cy.getBySel('toggle-request-transform').click({
force: true,
@ -47,19 +41,6 @@ export const checkTransformRequestUrlError = (
}
};
export const typeIntoContextAreaEnvVars = (
envVars: { key: string; value: string }[]
) => {
envVars.forEach((q, i) => {
cy.getBySel(`transform-env-vars-kv-key-${i}`).type(q.key, {
parseSpecialCharSequences: false,
});
cy.getBySel(`transform-env-vars-kv-value-${i}`).type(q.value, {
parseSpecialCharSequences: false,
});
});
};
export const typeIntoRequestQueryParams = (
queryParams: { key: string; value: string }[]
) => {
@ -99,89 +80,3 @@ export const checkTransformRequestBodyError = (exists: boolean) => {
cy.getBySel('transform-requestBody-error').should('not.exist');
}
};
export const getActionTransfromV1RequestBody = (actionName: string) => ({
type: 'bulk',
source: 'default',
args: [
{
type: 'set_custom_types',
args: {
scalars: [],
input_objects: [
{
name: 'SampleInput',
description: null,
fields: [
{
name: 'username',
type: 'String!',
description: null,
},
{
name: 'password',
type: 'String!',
description: null,
},
],
},
],
objects: [
{
name: 'SampleOutput',
description: null,
fields: [
{
name: 'accessToken',
type: 'String!',
description: null,
},
],
},
{
name: 'LoginResponse',
fields: [
{
name: 'accessToken',
type: 'String!',
},
],
},
],
enums: [],
},
},
{
type: 'create_action',
args: {
name: actionName,
definition: {
arguments: [
{
name: 'arg1',
type: 'SampleInput!',
description: null,
},
],
kind: 'synchronous',
output_type: 'SampleOutput',
handler: 'http://host.docker.internal:3000',
type: 'mutation',
headers: [],
timeout: null,
request_transform: {
version: 1,
template_engine: 'Kriti',
method: 'GET',
url: '{{$base_url}}/users',
query_params: {},
body:
'{\n "users": {\n "name": {{$body.input.arg1.username}}\n }\n}',
content_type: 'application/json',
},
},
comment: null,
},
},
],
});

View File

@ -68,21 +68,13 @@ if (testMode !== 'cli') {
parseSpecialCharSequences: false,
});
// Please note: we should wait for the outgoing request but at the moment of writing, I'm not
// sure the Console locally works as the one in CI. The request was `test_webhook_transform`
// --------------------
// Pleas note that the custom timeout is not checked explicitly checked, but asserting on
// the payload (see the next cy.intercept) means asserting on it too
cy.log('**--- Type in the Custom Timeout field**');
cy.getBySel('action-timeout-seconds').clear().type('25');
// Due to the double server/cli mode behavior, we do not assert about the XHR request payload here
// --------------------
cy.log('**--- Click the Create button**');
cy.getBySel('create-action-btn').click();
// Please note: we should wait for the outgoing request but at the moment of writing, I'm not
// sure the Console locally works as the one in CI. The request was `create_action`
// Due to the double server/cli mode behavior, we do not assert about the XHR request payload here
// --------------------
cy.log('**--- Check if the success notification is visible**');
@ -123,8 +115,7 @@ if (testMode !== 'cli') {
cy.log('**--- Click Save Permissions**');
cy.getBySel('save-permissions-for-action').click();
// Please note: we should wait for the outgoing request but at the moment of writing, I'm not
// sure the Console locally works as the one in CI. The request was `create_action_permission`
// Due to the double server/cli mode behavior, we do not assert about the XHR request payload here
// --------------------
cy.log('**--- Check if the success notification is visible**');
@ -163,8 +154,7 @@ if (testMode !== 'cli') {
cy.log('**--- Check the prompt has been called**');
cy.window().its('prompt').should('be.called');
// Please note: we should wait for the outgoing request but at the moment of writing, I'm not
// sure the Console locally works as the one in CI. The request was `drop_action`
// Due to the double server/cli mode behavior, we do not assert about the XHR request payload here
// --------------------
cy.log('**--- Check if the success notification is visible**');

View File

@ -70,15 +70,13 @@ if (testMode !== 'cli') {
parseSpecialCharSequences: false,
});
// Please note: we should wait for the outgoing request but at the moment of writing, I'm not
// sure the Console locally works as the one in CI. The request was `test_webhook_transform`
// Due to the double server/cli mode behavior, we do not assert about the XHR request payload here
// --------------------
cy.log('**--- Click the Create button**');
cy.getBySel('create-action-btn').click();
// Please note: we should wait for the outgoing request but at the moment of writing, I'm not
// sure the Console locally works as the one in CI. The request was `create_action`
// Due to the double server/cli mode behavior, we do not assert about the XHR request payload here
// --------------------
cy.log('**--- Check if the success notification is visible**');
@ -133,8 +131,6 @@ if (testMode !== 'cli') {
cy.log('**--- Click Save Permissions**');
cy.getBySel('save-permissions-for-action').click();
// sure the Console locally works as the one in CI. The request was `create_action_permission`
// --------------------
cy.log('**--- Check if the success notification is visible**');
cy.get(
@ -168,8 +164,7 @@ if (testMode !== 'cli') {
cy.log('**--- Click the Delete button**');
cy.getBySel('delete-action').click();
// Please note: we should wait for the outgoing request but at the moment of writing, I'm not
// sure the Console locally works as the one in CI. The request was `drop_action`
// Due to the double server/cli mode behavior, we do not assert about the XHR request payload here
// --------------------
cy.log('**--- Check if the success notification is visible**');

View File

@ -1,371 +0,0 @@
import { baseUrl, getElementFromClassName } from '../../helpers/dataHelpers';
import { setPromptValue } from '../../helpers/common';
import { AWAIT_LONG, AWAIT_SHORT } from '../../helpers/constants';
import { getTimeoutSeconds } from '../../helpers/eventHelpers';
import {
toggleRequestTransformSection,
togglePayloadTransformSection,
typeIntoRequestQueryParams,
typeIntoRequestUrl,
typeIntoTransformBody,
checkTransformRequestUrlError,
checkTransformRequestBodyError,
checkTransformRequestUrlPreview,
clearPayloadTransformBody,
clearRequestUrl,
toggleContextArea,
typeIntoContextAreaEnvVars,
getActionTransfromV1RequestBody,
} from '../../helpers/webhookTransformHelpers';
const ACTION_REQUEST_BODY_TRANSFORM_TEXTAREA = 4;
const statements = {
createMutationGQLQuery: `mutation getAccessToken ($username: String!, $password: String!) {
login (username: $username, password: $password) {
accessToken
`,
createMutationQueryVars: `{"username": "john", "password": "p"`,
createQueryActionText: `type Query {
addNumbers (numbers: [Int]): AddResult
}`,
createQueryActionCustomType: `type AddResult {
sum: Int
}`,
createQueryHandler: 'https://hasura-actions-demo.glitch.me/addNumbers',
createQueryGQLQuery: `query {
addNumbers(numbers: [1, 2, 3, 4]) {
sum
`,
changeHandlerText: 'http://host.docker.internal:3000',
createActionTransform: `type Mutation {
login (username: String!, password: String!): LoginResponse
}
`,
createTransformCustomType: `type LoginResponse {
accessToken: String!
}
`,
createTransformEnvHandler: '{{MY_WEBHOOK}}',
createTransformHandler: 'https://hasura-actions-demo.glitch.me',
createTransformIncorrectPayloadBody: `
{
"userInfo": {
"name": {{$input.username}}
`,
createTransformPayloadBody: `
{
"userInfo": {
"name": {{$body.input.username}},
"password": {{$body.input.password}},
"type": {{$body.action.name}}
`,
};
// NOTE: This test suite does not include cases for relationships, headers and
// the codegen part
const clearActionDef = () => {
cy.get('textarea').first().type('{selectall}', { force: true });
cy.get('textarea').first().trigger('keydown', {
keyCode: 46,
which: 46,
force: true,
});
};
const clearActionTypes = () => {
cy.get('textarea').eq(1).type('{selectall}', { force: true });
cy.get('textarea').eq(1).trigger('keydown', {
keyCode: 46,
which: 46,
force: true,
});
};
const clearHandler = () => {
cy.getBySel('action-create-handler-input').type('{selectall}{backspace}', {
force: true,
});
};
const typeIntoActionDef = (content: string) => {
cy.get('textarea').first().type(content, { force: true });
};
const typeIntoActionTypes = (content: string) => {
cy.get('textarea').eq(1).type(content, { force: true });
};
const typeIntoHandler = (content: string) => {
cy.getBySel('action-create-handler-input').type(content, {
force: true,
parseSpecialCharSequences: false,
});
};
const clickOnCreateAction = () => {
cy.getBySel('create-action-btn').scrollIntoView();
// hard await before accessing the element
cy.wait(AWAIT_SHORT);
cy.getBySel('create-action-btn').click({ force: true });
cy.wait(AWAIT_SHORT);
cy.get('.notification', { timeout: AWAIT_LONG })
.should('be.visible')
.and('contain', 'Created action successfully');
};
export const routeToGraphiql = () => {
cy.visit('/api/api-explorer');
cy.url({ timeout: AWAIT_LONG }).should('eq', `${baseUrl}/api/api-explorer`);
};
export const verifyMutation = () => {
routeToGraphiql();
// Type the query
cy.on('uncaught:exception', () => {
// NOTE: doing this since, there was some exception thrown by the
// graphiql editor even though the query was good.
// Docs: https://docs.cypress.io/api/events/catalog-of-events.html#To-turn-off-all-uncaught-exception-handling
return false;
});
cy.get('textarea')
.eq(0)
.type(`{enter}{uparrow}${statements.createMutationGQLQuery}`, {
force: true,
});
cy.get('textarea')
.eq(1)
.type(`{enter}{uparrow}${statements.createMutationQueryVars}`, {
force: true,
});
cy.get(getElementFromClassName('execute-button')).click();
// FIXME: NOT GOOD!
cy.get('.cm-property').contains('login');
cy.get('.cm-property').contains('accessToken');
cy.get('.cm-string').contains('Ew8jkGCNDGAo7p35RV72e0Lk3RGJoJKB');
};
const deleteAction = (promptValue: string) => {
setPromptValue(promptValue);
cy.getBySel('delete-action').click();
cy.window().its('prompt').should('be.called');
};
export const createQueryAction = () => {
// Routing to the index page
cy.visit('/actions/manage/actions');
cy.intercept('*', req => {
// send all other requests to the destination server
req.reply();
});
cy.url({ timeout: AWAIT_LONG }).should(
'eq',
`${baseUrl}/actions/manage/actions`
);
// Click on create
cy.getBySel('data-create-actions').click();
// Clear default text on
clearActionDef();
// type statement
typeIntoActionDef(statements.createQueryActionText);
// clear defaults on action types
clearActionTypes();
// type the action type text
typeIntoActionTypes(statements.createQueryActionCustomType);
// clear handler
clearHandler();
// type into handler
typeIntoHandler(statements.createQueryHandler);
// click to create action
clickOnCreateAction();
};
export const verifyQuery = () => {
cy.on('uncaught:exception', () => {
// NOTE: doing this since, there was some exception thrown by the
// graphiql editor even though the query was good.
// Docs: https://docs.cypress.io/api/events/catalog-of-events.html#To-turn-off-all-uncaught-exception-handling
return false;
});
routeToGraphiql();
cy.get('textarea')
.eq(0)
.type(`{enter}{uparrow}${statements.createQueryGQLQuery}`, { force: true })
.wait(4000);
cy.get(getElementFromClassName('execute-button')).click();
cy.get('.cm-property').contains('addNumbers');
cy.get('.cm-property').contains('sum');
cy.get('.cm-number').contains('10');
};
export const modifyQueryAction = () => {
cy.visit('/actions/manage/addNumbers/modify');
cy.url({ timeout: AWAIT_LONG }).should(
'eq',
`${baseUrl}/actions/manage/addNumbers/modify`
);
clearHandler();
typeIntoHandler(statements.changeHandlerText);
cy.getBySel('save-modify-action-changes').click();
// permissions part
cy.getBySel('actions-permissions').click();
cy.getBySel('role-textbox').type('MANAGER');
cy.getBySel('MANAGER-Permission').click();
cy.getBySel('save-permissions-for-action').click();
cy.getBySel('actions-modify').click();
};
export const deleteQueryAction = () => deleteAction('addNumbers');
export const createActionTransform = () => {
// Click on create
cy.getBySel('actions-sidebar-add-table').click();
// Clear default text on
clearActionDef();
// type statement
typeIntoActionDef(statements.createActionTransform);
// clear defaults on action types
clearActionTypes();
// type the action type text
typeIntoActionTypes(statements.createTransformCustomType);
cy.getBySel('action-timeout-seconds').clear().type(getTimeoutSeconds());
// open request transform section
toggleRequestTransformSection();
cy.wait(AWAIT_SHORT);
cy.getBySel('transform-POST').click();
// give correct body without webhook handler
clearHandler();
typeIntoRequestUrl('users');
cy.wait(AWAIT_SHORT);
// check for error
checkTransformRequestUrlError(
true,
'Please configure your webhook handler to generate request url transform'
);
// give correct body with env var
clearHandler();
typeIntoHandler(statements.createTransformEnvHandler);
// give body without specifying env var
clearRequestUrl();
typeIntoRequestUrl('/users');
cy.wait(AWAIT_SHORT);
// check for error
checkTransformRequestUrlError(true);
// add env var in context area
toggleContextArea();
typeIntoContextAreaEnvVars([
{ key: 'MY_WEBHOOK', value: 'https://handler.com' },
]);
// check there is no error and preview works fine
checkTransformRequestUrlError(false);
checkTransformRequestUrlPreview('https://handler.com/users');
// clear handler
clearHandler();
// type into handler
typeIntoHandler(statements.createTransformHandler);
// give incorrect body
clearRequestUrl();
typeIntoRequestUrl('{{$url}}/users');
cy.wait(AWAIT_SHORT);
// check for error
checkTransformRequestUrlError(true);
// give correct body
clearRequestUrl();
typeIntoRequestUrl('/{{$body.action.name}}');
cy.wait(AWAIT_SHORT);
typeIntoRequestQueryParams([
{ key: 'id', value: '5' },
{ key: 'name', value: '{{$body.action.name}}' },
]);
cy.wait(AWAIT_SHORT);
// check there is no error
checkTransformRequestUrlError(false);
// check the preview is correctly shown
checkTransformRequestUrlPreview(
'https://hasura-actions-demo.glitch.me/login?name=login&id=5'
);
// open payload transform section
togglePayloadTransformSection();
// give incorrect body
clearPayloadTransformBody(ACTION_REQUEST_BODY_TRANSFORM_TEXTAREA);
typeIntoTransformBody(
statements.createTransformIncorrectPayloadBody,
ACTION_REQUEST_BODY_TRANSFORM_TEXTAREA
);
cy.wait(AWAIT_SHORT);
checkTransformRequestBodyError(true);
// give correct body
clearPayloadTransformBody(ACTION_REQUEST_BODY_TRANSFORM_TEXTAREA);
typeIntoTransformBody(
statements.createTransformPayloadBody,
ACTION_REQUEST_BODY_TRANSFORM_TEXTAREA
);
cy.wait(AWAIT_SHORT);
checkTransformRequestBodyError(false);
// click to create action
cy.intercept('*', req => {
// send all other requests to the destination server
req.reply();
});
clickOnCreateAction();
cy.getBySel('action-timeout-seconds').should('have.value', '25');
};
export const deleteActionTransform = () => deleteAction('login');
const createV1ActionTransform = (actionName: string) => {
cy.request(
'POST',
'http://localhost:8080/v1/metadata',
getActionTransfromV1RequestBody(actionName)
).then(response => {
expect(response.body).to.not.be.null;
expect(response.body).to.be.a('array');
expect(response.body[0]).to.have.property('message', 'success'); // true
});
};
export const modifyV1ActionTransform = () => {
// Creates an action with v1 transform
createV1ActionTransform('login');
cy.wait(AWAIT_SHORT);
// modify and save the action, the action should be converted into v2
cy.visit('/actions/manage/login/modify');
cy.url({ timeout: AWAIT_LONG }).should(
'eq',
`${baseUrl}/actions/manage/login/modify`
);
cy.getBySel('transform-POST').click();
cy.getBySel('transform-requestUrl')
.clear()
.type('/{{$body.action.name}}/actions', {
parseSpecialCharSequences: false,
});
cy.getBySel('save-modify-action-changes').click();
cy.get('.notification', { timeout: AWAIT_LONG })
.should('be.visible')
.and('contain', 'Action saved successfully');
// delete the action
deleteActionTransform();
};

View File

@ -1,62 +1,59 @@
import { modifyV1ActionTransform } from './spec';
import { testMode } from '../../helpers/common';
import { setMetaData } from '../validators/validators';
const setup = () => {
describe.skip('Setup route', () => {
it('Visit the index route', () => {
cy.visit('/actions/manage/actions');
// Get and set validation metadata
setMetaData();
});
});
};
// const setup = () => {
// describe.skip('Setup route', () => {
// it('Visit the index route', () => {
// cy.visit('/actions/manage/actions');
// // Get and set validation metadata
// setMetaData();
// });
// });
// };
// TODO: what about the codegen part? Why is it not tested?
export const runActionsTests = () => {
describe.skip('Actions', () => {
// The test has been moved to mutationAction.e2e.test
// it('Create Mutation Action', createMutationAction);
// export const runActionsTests = () => {
// describe.skip('Actions', () => {
// The test has been moved to mutationAction.e2e.test
// it('Create Mutation Action', createMutationAction);
// The test was commented before moving the other ones to mutationAction.e2e.test
// it('Verify Mutation Actions on GraphiQL', verifyMutation);
// The test was commented before moving the other ones to mutationAction.e2e.test
// it('Verify Mutation Actions on GraphiQL', verifyMutation);
// The test has been moved to mutationAction.e2e.test
// it('Modify Mutation Action', modifyMutationAction);
// The test has been moved to mutationAction.e2e.test
// it('Modify Mutation Action', modifyMutationAction);
// The test has been moved to mutationAction.e2e.test
// it('Delete Mutation Action', deleteMutationAction);
// The test has been moved to mutationAction.e2e.test
// it('Delete Mutation Action', deleteMutationAction);
// The test has been moved to queryAction.e2e.test.e2e.test
// it('Create Query Action', createQueryAction);
// The test has been moved to queryAction.e2e.test.e2e.test
// it('Create Query Action', createQueryAction);
// The test was commented before moving the other ones to queryAction.e2e.test
// it('Verify Query Actions on GraphiQL', verifyQuery);
// The test was commented before moving the other ones to queryAction.e2e.test
// it('Verify Query Actions on GraphiQL', verifyQuery);
// The test has been moved to queryAction.e2e.test.e2e.test
// it('Modify Query Action', modifyQueryAction);
// The test has been moved to queryAction.e2e.test.e2e.test
// it('Modify Query Action', modifyQueryAction);
// The test has been moved to queryAction.e2e.test.e2e.test
// it('Delete Query Action', deleteQueryAction);
// The test has been moved to queryAction.e2e.test.e2e.test
// it('Delete Query Action', deleteQueryAction);
// The test has been moved to actionWithTransform.e2e.test.ts
// it('Create Action With Transform', createActionTransform);
// The test has been moved to actionWithTransform.e2e.test.ts
// it('Create Action With Transform', createActionTransform);
// The test has been moved to actionWithTransform.e2e.test.ts
// it('Update Action With Transform', modifyActionTransform);
// The test has been moved to actionWithTransform.e2e.test.ts
// it('Update Action With Transform', modifyActionTransform);
// The test has been moved to actionWithTransform.e2e.test.ts
// it('Delete Action With Transform', deleteActionTransform);
// The test has been moved to actionWithTransform.e2e.test.ts
// it('Delete Action With Transform', deleteActionTransform);
it(
'Create an action with V1 Transform and edit it through console, which will lead to the action being saved as V2',
modifyV1ActionTransform
);
});
};
// The test has been moved to v1ActionWithTransform.e2e.test.ts
// it(
// 'Create an action with V1 Transform and edit it through console, which will lead to the action being saved as V2',
// modifyV1ActionTransform
// );
// });
// };
if (testMode !== 'cli') {
setup();
runActionsTests();
}
// if (testMode !== 'cli') {
// setup();
// runActionsTests();
// }

View File

@ -56,12 +56,6 @@ if (testMode !== 'cli') {
{ force: true, delay: 0 }
);
// --------------------
// Pleas note that the custom timeout is not checked explicitly checked, but asserting on
// the payload (see the next cy.intercept) means asserting on it too
cy.log('**--- Type in the Custom Timeout field**');
cy.getBySel('action-timeout-seconds').clearConsoleTextarea().type('25');
// --------------------
cy.log('**--- Click the Add Request Options Transform button**');
cy.contains('Add Request Options Transform').click();
@ -79,8 +73,7 @@ if (testMode !== 'cli') {
cy.log('**--- Type in the Request URL Template field**');
cy.get('[placeholder="URL Template (Optional)..."]').type('users');
// Please note: we should wait for the outgoing request but at the moment of writing, I'm not
// sure the Console locally works as the one in CI.
// Due to the double server/cli mode behavior, we do not assert about the XHR request payload here
// --------------------
cy.log('**--- Look for the "Invalid URL" error**');
@ -112,8 +105,7 @@ if (testMode !== 'cli') {
.clearConsoleTextarea()
.type('/users');
// Please note: we should wait for the outgoing request but at the moment of writing, I'm not
// sure the Console locally works as the one in CI.
// Due to the double server/cli mode behavior, we do not assert about the XHR request payload here
// --------------------
cy.log('**--- Look for "Missing Env Var" error**');
@ -141,8 +133,7 @@ if (testMode !== 'cli') {
delay: 1,
});
// Please note: we should wait for the outgoing request but at the moment of writing, I'm not
// sure the Console locally works as the one in CI.
// Due to the double server/cli mode behavior, we do not assert about the XHR request payload here
// --------------------
cy.log('**--- Check the error disappeared**');
@ -183,8 +174,7 @@ if (testMode !== 'cli') {
.type('{{$url}}/users', { parseSpecialCharSequences: false });
});
// Please note: we should wait for the outgoing request but at the moment of writing, I'm not
// sure the Console locally works as the one in CI.
// Due to the double server/cli mode behavior, we do not assert about the XHR request payload here
// --------------------
cy.log('**--- Look for "Invalid Path" error**');
@ -220,8 +210,7 @@ if (testMode !== 'cli') {
}
);
// Please note: we should wait for the outgoing request but at the moment of writing, I'm not
// sure the Console locally works as the one in CI.
// Due to the double server/cli mode behavior, we do not assert about the XHR request payload here
// --------------------
cy.log('**--- Check the error disappeared**');
@ -270,8 +259,7 @@ if (testMode !== 'cli') {
{ force: true, delay: 1, parseSpecialCharSequences: false }
);
// Please note: we should wait for the outgoing request but at the moment of writing, I'm not
// sure the Console locally works as the one in CI.
// Due to the double server/cli mode behavior, we do not assert about the XHR request payload here
// --------------------
cy.log('**--- Look for the "Invalid path" error**');
@ -304,8 +292,7 @@ if (testMode !== 'cli') {
{ force: true, delay: 1, parseSpecialCharSequences: false }
);
// Please note: we should wait for the outgoing request but at the moment of writing, I'm not
// sure the Console locally works as the one in CI.
// Due to the double server/cli mode behavior, we do not assert about the XHR request payload here
// --------------------
cy.log('**--- Check the error disappeared**');
@ -321,8 +308,7 @@ if (testMode !== 'cli') {
cy.log('**--- Click the Create button**');
cy.getBySel('create-action-btn').click();
// Please note: we should wait for the outgoing request but at the moment of writing, I'm not
// sure the Console locally works as the one in CI. The request was `create_action`
// Due to the double server/cli mode behavior, we do not assert about the XHR request payload here
// --------------------
cy.log('**--- Check if the success notification is visible**');
@ -376,8 +362,7 @@ if (testMode !== 'cli') {
cy.log('**--- Click on the Save button**');
cy.getBySel('save-modify-action-changes').click();
// Please note: we should wait for the outgoing request but at the moment of writing, I'm not
// sure the Console locally works as the one in CI. The request was `create_action_permission`
// Due to the double server/cli mode behavior, we do not assert about the XHR request payload here
// --------------------
cy.log('**--- Check if the success notification is visible**');
@ -427,8 +412,7 @@ if (testMode !== 'cli') {
cy.log('**--- Check the prompt has been called**');
cy.window().its('prompt').should('be.called');
// Please note: we should wait for the outgoing request but at the moment of writing, I'm not
// sure the Console locally works as the one in CI. The request was `drop_action`
// Due to the double server/cli mode behavior, we do not assert about the XHR request payload here
// --------------------
cy.log('**--- Check if the success notification is visible**');

View File

@ -0,0 +1,13 @@
/**
* Delete the Action straight from the server.
*/
export function deleteV1LoginAction() {
Cypress.log({ message: '**--- Action delete: start**' });
return cy
.request('POST', 'http://localhost:8080/v1/metadata', {
type: 'drop_action',
args: { name: 'v1Login' },
})
.then(() => Cypress.log({ message: '**--- Action delete: end**' }));
}

View File

@ -0,0 +1,122 @@
import { readMetadata } from '../services/readMetadata';
import { deleteLoginAction } from '../services/deleteLoginAction';
/**
* Ensure the V1 Action exists.
*/
export function v1LoginActionMustExist() {
Cypress.log({ message: '**--- Action check: start**' });
readMetadata().then(response => {
const actionExists = !!response.body.actions?.find(
// TODO: properly type it
action => action.name === 'TODO:'
);
if (actionExists) {
Cypress.log({ message: '**--- The action exists**' });
Cypress.log({ message: '**--- Action check: end**' });
return;
}
Cypress.log({ message: '**--- The action does not exist**' });
cy.request('POST', 'http://localhost:8080/v1/metadata', {
type: 'bulk',
source: 'default',
args: [
{
type: 'set_custom_types',
args: {
scalars: [],
input_objects: [
{
name: 'SampleInput',
description: null,
fields: [
{
name: 'username',
type: 'String!',
description: null,
},
{
name: 'password',
type: 'String!',
description: null,
},
],
},
],
objects: [
{
name: 'SampleOutput',
description: null,
fields: [
{
name: 'accessToken',
type: 'String!',
description: null,
},
],
},
{
name: 'LoginResponse',
fields: [
{
name: 'accessToken',
type: 'String!',
},
],
},
],
enums: [],
},
},
{
type: 'create_action',
args: {
name: 'v1Login',
definition: {
arguments: [
{
name: 'arg1',
type: 'SampleInput!',
description: null,
},
],
kind: 'synchronous',
output_type: 'SampleOutput',
handler: 'http://host.docker.internal:3000',
type: 'mutation',
headers: [],
timeout: null,
request_transform: {
version: 1,
template_engine: 'Kriti',
method: 'GET',
url: '{{$base_url}}/users',
query_params: {},
body:
'{\n "users": {\n "name": {{$body.input.arg1.username}}\n }\n}',
content_type: 'application/json',
},
},
comment: null,
},
},
],
})
.then(response => {
expect(response.body, 'Response body exists').to.not.be.null;
expect(response.body, 'Response body is an array').to.be.a('array');
expect(
response.body[0],
'Response body contains success'
).to.have.property('message', 'success');
})
.then(() => {
Cypress.log({ message: '**--- The action has been created**' });
Cypress.log({ message: '**--- Action check: end**' });
});
});
}

View File

@ -0,0 +1,21 @@
import { readMetadata } from '../services/readMetadata';
import { deleteV1LoginAction } from '../services/deleteV1LoginAction';
/**
* Ensure the Action does not exist.
*/
export function v1LoginActionMustNotExist() {
Cypress.log({ message: '**--- Action check: start**' });
readMetadata().then(response => {
const actionExists = !!response.body.actions?.find(
// TODO: properly type it
action => action.name === 'v1Login'
);
if (actionExists) {
Cypress.log({ message: '**--- The Action must be deleted**' }),
deleteV1LoginAction();
}
});
}

View File

@ -0,0 +1,100 @@
import { testMode } from '../../../helpers/common';
import { logMetadataRequests } from './utils/requests/logMetadataRequests';
import { v1LoginActionMustExist } from './utils/testState/v1LoginActionMustExist';
import { v1LoginActionMustNotExist } from './utils/testState/v1LoginActionMustNotExist';
if (testMode !== 'cli') {
describe('V1 Actions with Transform', () => {
before(() => {
v1LoginActionMustExist();
logMetadataRequests();
cy.visit('/actions/manage/v1Login/modify');
});
after(() => {
// Delete the created action, if any
v1LoginActionMustNotExist();
});
it('When the users edit a V1 Action with Transform, everything should work', () => {
cy.log('**------------------------------**');
cy.log('**------------------------------**');
cy.log('**------------------------------**');
cy.log('**--- Step 3: Mutation Action delete**');
cy.log('**------------------------------**');
cy.log('**------------------------------**');
cy.log('**------------------------------**');
cy.get('[data-cy="Change Request Options"]').within(() => {
// --------------------
cy.log('**--- Choose POST**');
cy.contains('POST').click();
// --------------------
cy.log('**--- Type in the Request URL Template field**');
cy.get('[placeholder="URL Template (Optional)..."]').type(
'/{{$body.action.name}}/actions',
{
delay: 0,
parseSpecialCharSequences: false,
}
);
});
// --------------------
cy.log('**--- Click on the Save button**');
cy.getBySel('save-modify-action-changes').click();
// --------------------
cy.log('**--- Check if the success notification is visible**');
cy.get(
'.notification',
// The custom timeout aims to replace the lack of waiting for the outgoing request
{ timeout: 10000 }
)
.should('be.visible')
.and('contain', 'Action saved successfully');
cy.log('**------------------------------**');
cy.log('**------------------------------**');
cy.log('**------------------------------**');
cy.log('**--- Step 2: Action delete**');
cy.log('**------------------------------**');
cy.log('**------------------------------**');
cy.log('**------------------------------**');
// --------------------
cy.log('**--- Go the the action page**');
cy.getBySel('actions-table-links').within(() => {
cy.getBySel('v1Login').click();
});
// --------------------
cy.log('**--- Set the prompt value**');
cy.window().then(win => cy.stub(win, 'prompt').returns('v1Login'));
cy.log('**--- Click the Delete button**');
cy.getBySel('delete-action').click();
// --------------------
cy.log('**--- Check the prompt has been called**');
cy.window().its('prompt').should('be.called');
// Due to the double server/cli mode behavior, we do not assert about the XHR request payload here
// --------------------
cy.log('**--- Check if the success notification is visible**');
cy.get(
'.notification',
// The custom timeout aims to replace the lack of waiting for the outgoing request
{ timeout: 10000 }
)
.should('be.visible')
.and('contain', 'Action deleted successfully');
// TODO: check if it does not exist in the database? Other tests do that
});
});
}

View File

@ -32,7 +32,3 @@ import './clearConsoleTextarea';
Cypress.Commands.add('getBySel', (selector, ...args) => {
return cy.get(`[data-test=${selector}]`, ...args);
});
Cypress.Commands.add('getBySelLike', (selector, ...args) => {
return cy.get(`[data-test*=${selector}]`, ...args);
});

View File

@ -7,19 +7,13 @@ declare namespace Cypress {
* <button data-test="greeting"> </button>
* @example cy.getBySel('greeting')
*/
getBySel(value: string): Chainable<Element>;
/**
* Custom command to select DOM element by data-test* attribute.
* <button data-test="save_me_oh_God"> </button>
* @example cy.getBySelLike('save_me')
*/
getBySelLike(value: string): Chainable<Element>;
getBySel(value: string): Chainable<JQuery<Element>>;
/**
* Custom command to work around the fact that cy.clear sometimes fails at clearing the
* Console's textarea
* @example cy.get('textarea').clearConsoleTextarea()
*/
clearConsoleTextarea(): Chainable<Element>;
clearConsoleTextarea(): Chainable<JQuery<HTMLTextAreaElement>>;
/**
* Visit the initial empty page.
* Console's textarea