Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Onboarding] NV-3262 | Cypress tests #5235

Merged
merged 10 commits into from
Feb 27, 2024
8 changes: 8 additions & 0 deletions apps/web/cypress/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

type IMountType = import('cypress/react').mount;
type ICreateNotificationTemplateDto = import('@novu/shared').ICreateNotificationTemplateDto;
type FeatureFlagsKeysEnum = import('@novu/shared').FeatureFlagsKeysEnum;

declare namespace Cypress {
interface Chainable {
Expand Down Expand Up @@ -45,6 +46,13 @@ declare namespace Cypress {
*/
inviteUser(email: string): Chainable<Response>;

/**
* Intercept feature flags from LaunchDarkly and mock their response.
*
* Must be in beforeEach (vs before) because intercepts are cleared before each test run.
*/
mockFeatureFlags(featureFlags: Partial<Record<FeatureFlagsKeysEnum, boolean>>): Chainable<void>;

loginWithGitHub(): Chainable<any>;

makeBlueprints(): Chainable<any>;
Expand Down
92 changes: 91 additions & 1 deletion apps/web/cypress/plugins/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,14 @@ import {
EnvironmentService,
} from '@novu/testing';
import { JobsService } from '@novu/testing';
import { ChannelTypeEnum, getPopularTemplateIds, ProvidersIdEnum } from '@novu/shared';
import {
ChannelTypeEnum,
getPopularTemplateIds,
getGetStartedTemplateIds,
ProvidersIdEnum,
TriggerTypeEnum,
StepTypeEnum,
} from '@novu/shared';

const jobsService = new JobsService();

Expand Down Expand Up @@ -198,6 +205,11 @@ module.exports = (on, config) => {
_environmentId: productionEnvironmentId,
_organizationId: organizationId,
});
const productionGetStartedGroup = await notificationGroupRepository.findOne({
name: 'Get started',
_environmentId: productionEnvironmentId,
_organizationId: organizationId,
});

if (!productionGeneralGroup) {
await notificationGroupRepository.create({
Expand All @@ -206,6 +218,13 @@ module.exports = (on, config) => {
_organizationId: organizationId,
});
}
if (!productionGetStartedGroup) {
await notificationGroupRepository.create({
name: 'Get started',
_environmentId: productionEnvironmentId,
_organizationId: organizationId,
});
}

const productionNotificationTemplateService = new NotificationTemplateService(
user._id,
Expand All @@ -214,6 +233,7 @@ module.exports = (on, config) => {
);

const popularTemplateIds = getPopularTemplateIds({ production: false });
const getStartedTemplateIds = getGetStartedTemplateIds({ production: false });

const blueprintTemplates = await productionNotificationTemplateService.getBlueprintTemplates(
organizationId,
Expand All @@ -236,6 +256,76 @@ module.exports = (on, config) => {
name: ':fa-solid fa-lock: Password reset',
isBlueprint: true,
}),
productionNotificationTemplateService.createTemplate({
_id: getStartedTemplateIds[0],
noFeedId: true,
noLayoutId: true,
name: ':fa-solid fa-clock: Delay',
isBlueprint: true,
steps: [
{
type: StepTypeEnum.DELAY,
name: 'Delay',
content: '',
},
],
triggers: [
{
identifier: 'get-started-delay',
type: TriggerTypeEnum.EVENT,
variables: [],
},
],
}),
productionNotificationTemplateService.createTemplate({
_id: getStartedTemplateIds[1],
noFeedId: true,
noLayoutId: true,
name: ':fa-solid fa-layer-group: Digest',
isBlueprint: true,
steps: [
{
type: StepTypeEnum.DIGEST,
name: 'Digest',
content: '',
},
],
triggers: [
{
identifier: 'get-started-digest',
type: TriggerTypeEnum.EVENT,
variables: [],
},
],
}),
productionNotificationTemplateService.createTemplate({
_id: getStartedTemplateIds[2],
noFeedId: true,
noLayoutId: true,
name: ':fa-solid fa-bell: In-App',
isBlueprint: true,
triggers: [
{
identifier: 'get-started-in-app',
type: TriggerTypeEnum.EVENT,
variables: [],
},
],
}),
productionNotificationTemplateService.createTemplate({
_id: getStartedTemplateIds[3],
noFeedId: true,
noLayoutId: true,
name: ':fa-solid fa-earth-americas: Multi-channel',
isBlueprint: true,
triggers: [
{
identifier: 'get-started-multi-channel',
type: TriggerTypeEnum.EVENT,
variables: [],
},
],
}),
]);
}

Expand Down
29 changes: 28 additions & 1 deletion apps/web/cypress/support/commands.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// load the global Cypress types
/// <reference types="cypress" />

import { MemberRoleEnum, MemberStatusEnum } from '@novu/shared';
import { MemberRoleEnum, MemberStatusEnum, FeatureFlagsKeysEnum } from '@novu/shared';
import 'cypress-wait-until';

Cypress.Commands.add('getByTestId', (selector, ...args) => {
Expand Down Expand Up @@ -167,4 +167,31 @@ Cypress.Commands.add('loginWithGitHub', () => {
);
});

/**
* Intercept feature flags from LaunchDarkly and mock their response.
*
* Must be in beforeEach (vs before) because intercepts are cleared before each test run.
* https://medium.com/@kutnickclose/how-to-use-cypress-with-launchdarkly-897349b7f976
*/
Cypress.Commands.add('mockFeatureFlags', (featureFlags: Partial<Record<FeatureFlagsKeysEnum, boolean>>) => {
// turn off push (EventSource) updates from LaunchDarkly
cy.intercept({ hostname: /.*clientstream.launchdarkly.com/ }, (req) => {
req.reply('data: no streaming feature flag data here\n\n', {
'content-type': 'text/event-stream; charset=utf-8',
});
});

// ignore api calls to events endpoint
cy.intercept({ hostname: /.*events.launchdarkly.com/ }, { body: {} });

// return feature flag values in format expected by launchdarkly client
cy.intercept({ hostname: /.*app.launchdarkly.com/ }, (req) => {
const body = {};
Object.entries(featureFlags).forEach(([featureFlagName, featureFlagValue]) => {
body[featureFlagName] = { value: featureFlagValue };
});
req.reply({ body });
});
});

export {};
Loading
Loading