/// // *********************************************** // This example commands.ts shows you how to // create various custom commands and overwrite // existing commands. // // For more comprehensive examples of custom // commands please read more here: // https://on.cypress.io/custom-commands // *********************************************** // // // -- This is a parent command -- // Cypress.Commands.add('login', (email, password) => { ... }) // // // -- This is a child command -- // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) // // // -- This is a dual command -- // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) // // // -- This will overwrite an existing command -- // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) // import { makeExecutableSchema } from '@graphql-tools/schema'; const typenameResolver = { __resolveType(obj, context, info) { return obj.__typename; }, }; const getByDataCy = (selector: string) => { return cy.get(`[data-cy=${selector}]`); }; const mockGraphql = (options?: any) => { cy.task('getSchema').then((schemaString: string) => { const resolverMap = { DeleteSnapshotResult: typenameResolver, UpdateSnapshotResult: typenameResolver, RedeemCouponResult: typenameResolver, }; const schema = makeExecutableSchema({ typeDefs: schemaString, resolvers: resolverMap, }); const schemaWithMocks = addMocksToSchema({ schema, mocks, preserveResolvers: true, }); let currentOperations = options && options.operations ? options.operations : {}; // rebuilding what was in cypress-graphql-mock package here, because now we can just intercept the graphql call instead of messing with fetch cy.intercept('POST', '/api/graphql', (req) => { const { operationName, query, variables } = req.body; return getRootValue(currentOperations, operationName, variables) .then((rootValue) => { if (!rootValue) { return req; } if ( // Additional checks here because of transpilation. // We will lose instanceof if we are not using specific babel plugin, or using pure TS to compile front-end rootValue instanceof GraphQLError || rootValue.constructor === GraphQLError || rootValue.constructor.name === 'GraphQLError' ) { return req.reply({ body: { data: {}, errors: [rootValue], }, }); } graphql({ schema: schemaWithMocks, source: query, variableValues: variables, operationName, rootValue, }).then( (result) => { req.reply({ ...result, }); }, (e) => { console.error(e); } ); }) .catch((e) => { console.error(e); req.reply({ statusCode: 500, body: { errors: ['Internal server error'] } }); }); }).as('graphqlRequest'); cy.wrap({ setOperations: (options: any) => { currentOperations = { ...currentOperations, ...options.operations, }; }, }).as('mockGraphqlOps'); }); }; const mockGraphqlOps = (options) => { cy.get('@mockGraphqlOps').invoke('setOperations' as any, options); }; const login = (username: string, password: string, visitLogin = false) => { if (visitLogin) { cy.visit('/beta-login'); } if (username !== '') { cy.get('[data-cy=email-input]').type(username); } if (password !== '') { cy.get('[data-cy=password-input]').type(password); } cy.get('[data-cy=login-button]').click(); }; const fakeLogin = () => { cy.log('Logging in (fake)'); cy.setCookie('loginStatus', 'true'); }; declare global { namespace Cypress { interface Chainable { /** * Login via API call to the GraphQL endpoint, without calling the frontend. Faster than the other login. * @param username * @param password * @example * cy.apolloLogin('ross.geller', 'test') */ apolloLogin(username: string, password: string): Chainable; /** * Selects an element based on the `data-cy=xxx` attribute * @param selector - The value of the data-cy attribute to select * @example * cy.getByDataCy('my-new-button') */ // getByDataCy(selector: string): Chainable; getByDataCy: typeof getByDataCy; selectClass(schoolClass: string): void; login: typeof login; fakeLogin: typeof fakeLogin; isSubmissionReadOnly(myText: string): void; openSidebar(): void; setup(): void; mockGraphql: typeof mockGraphql; mockGraphqlOps: typeof mockGraphqlOps; } } } // installed a fork of the original package, because of this issue: // https://github.com/tgriesser/cypress-graphql-mock/issues/23 // todo: once above issue is fixed, go back to the original repo -> npm install cypress-graphql-mock // import 'cypress-graphql-mock'; import mocks from '../fixtures/mocks'; import { addMocksToSchema } from '@graphql-tools/mock'; import { graphql, GraphQLError } from 'graphql'; Cypress.Commands.add('apolloLogin', (username, password) => { const payload = { operationName: 'BetaLogin', variables: { input: { usernameInput: username, passwordInput: password, }, }, query: 'mutation BetaLogin($input: BetaLoginInput!) {\n betaLogin(input: $input) {\n success\n __typename\n }\n}\n', }; cy.request({ method: 'POST', url: '/api/graphql-public/', body: payload, }); }); // todo: replace with apollo call Cypress.Commands.add('login', login); Cypress.Commands.add('getByDataCy', getByDataCy); Cypress.Commands.add('selectClass', (schoolClass) => { cy.getByDataCy('user-widget-avatar').click(); cy.getByDataCy('class-selection').click(); cy.getByDataCy('class-selection-entry').contains(schoolClass).click(); }); Cypress.Commands.add('fakeLogin', fakeLogin); Cypress.Commands.add('isSubmissionReadOnly', (myText) => { cy.get('.submission-form__textarea--readonly').as('textarea'); cy.get('@textarea').invoke('val').should('contain', myText); cy.get('@textarea').should('have.attr', 'readonly'); cy.getByDataCy('submission-form-submit').should('not.exist'); }); Cypress.Commands.add('openSidebar', () => { cy.getByDataCy('user-widget-avatar').click(); }); Cypress.Commands.add('setup', () => { cy.fakeLogin(); cy.viewport('macbook-15'); cy.mockGraphql(); }); Cypress.Commands.add('mockGraphql', mockGraphql); Cypress.Commands.add('mockGraphqlOps', mockGraphqlOps); const getRootValue = async (allOperations: any, operationName: string, variables: any) => { const operation = allOperations[operationName]; if (typeof operation === 'function') { return Promise.resolve().then(() => operation(variables)); } return Promise.resolve(operation); };