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

chore(dashboard): user journey smoke tests #7124

Open
wants to merge 28 commits into
base: next
Choose a base branch
from

Conversation

LetItRock
Copy link
Contributor

What changed? Why was the change needed?

Dashboard - the core user journeys smoke tests that cover:

  • dashboard-created workflow
  • code-created workflow

TODO (in the future):

  • we have one drawback: we need to establish independent clerk and api sessions for the tests, I left a comment about it in the code

Screenshots

Screenshot 2024-11-25 at 13 59 28

@LetItRock LetItRock self-assigned this Nov 25, 2024
Copy link

linear bot commented Nov 25, 2024

Copy link

netlify bot commented Nov 25, 2024

Deploy Preview for novu-stg-vite-dashboard-poc ready!

Name Link
🔨 Latest commit fa860f3
🔍 Latest deploy log https://app.netlify.com/sites/novu-stg-vite-dashboard-poc/deploys/674d7a4f4008e900085d165d
😎 Deploy Preview https://deploy-preview-7124.dashboard-v2.novu-staging.co
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

@@ -121,3 +121,4 @@ backups/

# Nest.js auto-generated metadata (https://docs.nestjs.com/recipes/swc#monorepo-and-cli-plugins)
src/metadata.ts
src/.env.test
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed the .env.test file from git, because the local file that I used for the tests had sensitive clerk credentials

@@ -28,4 +28,6 @@ tsconfig.node.tsbuildinfo
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
/playwright/
.env.test
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.env.test is the file for the Dashboard env variables

/playwright/.cache/
/playwright/
.env.test
.env.playwright
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.env.playwright is the file for the playwright that has all env variables to setup the clerk and api session

@@ -5,6 +5,7 @@
"type": "module",
"scripts": {
"start": "vite",
"start:test": "vite --mode test",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

run dashboard and use .env.test file

Comment on lines +91 to +92
"@novu/dal": "workspace:*",
"@novu/testing": "workspace:*",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

used to initialize the api session

Comment on lines +19 to +43
bridgeServer = new BridgeServer({ secretKey: session.environment.apiKeys[0].key, apiUrl: process.env.API_URL });

const newWorkflow = workflow(workflowId, async ({ step }) => {
await step.inApp(
inAppStepId,
async (controls) => {
return {
subject: `Hi ${controls.name}! You've been invited to join the Novu project`,
body,
};
},
{
controlSchema: {
type: 'object',
properties: {
name: { type: 'string', default: 'John' },
},
} as const,
}
);
});
await bridgeServer.start({ workflows: [newWorkflow] });
await session.testAgent.post(`/v1/bridge/sync`).send({
bridgeUrl: bridgeServer.serverPath,
});
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Run the bridge server and sync the workflow

await session.testAgent.post(`/v1/bridge/sync`).send({
bridgeUrl: bridgeServer.serverPath,
});
await page.waitForTimeout(2000);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be removed

Comment on lines +10 to +15
/**
* TODO: Currently we are running tests with a single Clerk user and organization.
* This approach has a drawback that the tests are not independent from each other and write the data to the same organization.
* We should consider creating a new Clerk user and organization for each test using the @clerk/backend package.
* Then initialize the BE session with the new Clerk user and organization and update Clerk user metadata from the newly created session.
*/
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hopefully, it explains the problem well

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct! We should leverage Clerk Backend API, create a user before every run, sign in with that user, and then delete the user at the end.

Regarding the need for Clerk webhooks during user and organization creation, I suggest connecting to Mongo directly from the test code using the response of the Clerk API to create the user and organization record the application needs to function.

On user deletion, I don't think we need to do anything in Mongo as we run in a dockerized environment.

@@ -0,0 +1,3 @@
{
"type": "commonjs"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dal and testing packages are CJS modules which can't be imported by the ESM modules.
So this is the trick (together with tsconfig.json) to say "please allow me to import" in my test files :D

private memberRepository = getEERepository<MemberRepository>('MemberRepository');
private memberRepository: MemberRepository;

constructor({ mockClerkClient = true }: { mockClerkClient?: boolean } = {}) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the Dashboard E2E tests we don't want to mock the Clerk client, because we initialize the Clerk session with a real user that has the org and metadata that is used by the Dashboard code.

Copy link

pkg-pr-new bot commented Nov 25, 2024

Open in Stackblitz

@novu/client

npm i https://pkg.pr.new/novuhq/novu/@novu/client@7124

@novu/framework

npm i https://pkg.pr.new/novuhq/novu/@novu/framework@7124

@novu/js

npm i https://pkg.pr.new/novuhq/novu/@novu/js@7124

@novu/nest

npm i https://pkg.pr.new/novuhq/novu/@novu/nest@7124

@novu/headless

npm i https://pkg.pr.new/novuhq/novu/@novu/headless@7124

@novu/nextjs

npm i https://pkg.pr.new/novuhq/novu/@novu/nextjs@7124

@novu/node

npm i https://pkg.pr.new/novuhq/novu/@novu/node@7124

@novu/notification-center

npm i https://pkg.pr.new/novuhq/novu/@novu/notification-center@7124

novu

npm i https://pkg.pr.new/novuhq/novu@7124

@novu/providers

npm i https://pkg.pr.new/novuhq/novu/@novu/providers@7124

@novu/react

npm i https://pkg.pr.new/novuhq/novu/@novu/react@7124

@novu/react-native

npm i https://pkg.pr.new/novuhq/novu/@novu/react-native@7124

@novu/shared

npm i https://pkg.pr.new/novuhq/novu/@novu/shared@7124

@novu/stateless

npm i https://pkg.pr.new/novuhq/novu/@novu/stateless@7124

commit: 5d0de32

@LetItRock LetItRock requested a review from a team as a code owner November 25, 2024 15:52
@LetItRock LetItRock requested review from scopsy and removed request for a team November 25, 2024 15:52
@@ -0,0 +1,115 @@
import { StepTypeEnum } from '@novu/shared';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about naming it from the user journey perspective? For example: manage-workflows.e2e.ts.

I added the e2e extension as we have the convention with e2e testing file naming on the API.

@@ -0,0 +1,90 @@
import { test } from '@playwright/test';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about naming it from the user journey perspective? For example: sync-workflow.e2e.ts.

await bridgeServer.stop();
});

test('code defined workflow user journey', async ({ page }) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
test('code defined workflow user journey', async ({ page }) => {
test('sync workflow', async ({ page }) => {

await initializeSession({ page });
});

test('dashboard defined workflow user journey', async ({ page }) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
test('dashboard defined workflow user journey', async ({ page }) => {
test('manage workflows', async ({ page }) => {

Comment on lines +10 to +15
/**
* TODO: Currently we are running tests with a single Clerk user and organization.
* This approach has a drawback that the tests are not independent from each other and write the data to the same organization.
* We should consider creating a new Clerk user and organization for each test using the @clerk/backend package.
* Then initialize the BE session with the new Clerk user and organization and update Clerk user metadata from the newly created session.
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct! We should leverage Clerk Backend API, create a user before every run, sign in with that user, and then delete the user at the end.

Regarding the need for Clerk webhooks during user and organization creation, I suggest connecting to Mongo directly from the test code using the response of the Clerk API to create the user and organization record the application needs to function.

On user deletion, I don't think we need to do anything in Mongo as we run in a dockerized environment.

}

async expectNameValidationError(): Promise<void> {
await expect(await this.page.getByText('Name is required')).toBeVisible();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Page object models are usually selectors and request helpers that keep the e2e code dry. To me, expectations belong to the test. Moreover, E2E tests have to be readable and self-explanatory. Dryness is not the most critical requirement.

So, I suggest adding all the expectations in the e2e file and using the page object models for selectors, event listeners and waiting logic. This ensures that page object models are highly reusable and can work with any expectation the e2e will require.

Most importantly, when the e2e is read by the future maintainer, they don't have to jump into every function to see what it does.

const workflowIdInput = await this.page.locator('input[name="workflowId"]');

// check the workflow id
await expect(await workflowIdInput.inputValue()).toEqual(workflowId);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Following on my previous comment, this is a good example of overloading a page object function with expectation logic that belongs to the test.

@@ -0,0 +1,10 @@
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the extra tsconfig.json required?

await page.addInitScript((currentSession) => {
window.addEventListener('DOMContentLoaded', () => {
localStorage.setItem('nv_auth_token', currentSession.token);
localStorage.setItem('nv_last_environment_id', currentSession.environment._id);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is no longer required in the new dashboard. We can safely remove it.


return new EEMemberRepository(new CommunityOrganizationRepository(), new ClerkClientMock());
if (mockClerkClient) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please refer to my previous comment about creating a Clerk user before each test run and doing an actual sign-in so that we can test the actual interaction between Clerk and the application, not a mocked version of it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants