Skip to content

Commit

Permalink
VR-263: connection_invite_admin template update.
Browse files Browse the repository at this point in the history
  • Loading branch information
n3op2 committed Nov 15, 2024
1 parent 51b8808 commit 12da48a
Show file tree
Hide file tree
Showing 17 changed files with 213 additions and 139 deletions.
188 changes: 94 additions & 94 deletions package-lock.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "veritable-ui",
"version": "0.14.16",
"version": "0.14.17",
"description": "UI for Veritable",
"main": "src/index.ts",
"type": "module",
Expand Down Expand Up @@ -64,7 +64,7 @@
"@playwright/test": "^1.48.2",
"@swc-node/register": "^1.10.9",
"@swc/cli": "^0.5.0",
"@swc/core": "^1.9.2",
"@swc/core": "^1.9.1",
"@types/body-parser": "^1.19.5",
"@types/chai": "^5.0.1",
"@types/chai-jest-snapshot": "^1.3.8",
Expand All @@ -78,8 +78,8 @@
"@types/supertest": "^6.0.2",
"@types/swagger-ui-express": "^4.1.7",
"@types/ws": "^8.5.13",
"@typescript-eslint/eslint-plugin": "^8.14.0",
"@typescript-eslint/parser": "^8.14.0",
"@typescript-eslint/eslint-plugin": "^8.13.0",
"@typescript-eslint/parser": "^8.13.0",
"chai": "^5.1.2",
"chai-jest-snapshot": "^2.0.0",
"concurrently": "^9.1.0",
Expand Down
1 change: 1 addition & 0 deletions playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { defineConfig, devices } from '@playwright/test'
*/
export default defineConfig({
testDir: './test/e2e',
snapshotPathTemplate: './test/e2e/snapshots/{testFilePath}/{arg}{ext}',
/* Run tests in files in parallel */
fullyParallel: false,
/* Fail the build on CI if you accidentally left test.only in the source code. */
Expand Down
22 changes: 16 additions & 6 deletions src/controllers/connection/__tests__/newConnection.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -360,11 +360,19 @@ describe('NewConnectionController', () => {
})

it('should send second email to admin', () => {
expect(emailSpy.firstCall.args[0]).equal('connection_invite')
expect(emailSpy.secondCall.args[0]).equal('connection_invite_admin')
expect(emailSpy.secondCall.args[1]?.address).equal(
'NAME, ADDRESS_LINE_1, ADDRESS_LINE_2, CARE_OF, LOCALITY, PO_BOX, POSTAL_CODE, COUNTRY, PREMISES, REGION'
)
expect(emailSpy.firstCall.args[1]).to.deep.contain({
to: '[email protected]',
toCompanyName: 'NAME',
fromCompanyName: 'COMPANY_NAME',
})
expect(emailSpy.secondCall.args[1]?.pin).match(/[0-9]{6}/)
expect(emailSpy.secondCall.args[1]).to.deep.contain({
invitator: 'NAME',
address:
'NAME, ADDRESS_LINE_1, ADDRESS_LINE_2, CARE_OF, LOCALITY, PO_BOX, POSTAL_CODE, COUNTRY, PREMISES, REGION',
})
})
})
})
Expand Down Expand Up @@ -545,10 +553,12 @@ describe('NewConnectionController', () => {

it('should send email to admin', () => {
expect(emailSpy.firstCall.args[0]).equal('connection_invite_admin')
expect(emailSpy.firstCall.args[1]?.address).equal(
'NAME, ADDRESS_LINE_1, ADDRESS_LINE_2, CARE_OF, LOCALITY, PO_BOX, POSTAL_CODE, COUNTRY, PREMISES, REGION'
)
expect(emailSpy.firstCall.args[1]?.pin).match(/[0-9]{6}/)
expect(emailSpy.firstCall.args[1]).to.deep.contain({
invitator: 'NAME',
address:
'NAME, ADDRESS_LINE_1, ADDRESS_LINE_2, CARE_OF, LOCALITY, PO_BOX, POSTAL_CODE, COUNTRY, PREMISES, REGION',
})
})
})
})
Expand Down
1 change: 1 addition & 0 deletions src/controllers/connection/newConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@ export class NewConnectionController extends HTMLController {
private async sendAdminEmail(company: CompanyProfile, pin: string) {
await neverFail(
this.email.sendMail('connection_invite_admin', {
invitator: company.company_name,
address: [
company.company_name,
company.registered_office_address.address_line_1,
Expand Down
2 changes: 1 addition & 1 deletion src/env/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const requiredProdEnvs = {
IDP_CLIENT_ID: 'veritable',
IDP_INTERNAL_URL_PREFIX: 'http://localhost:3080/realms/veritable/protocol/openid-connect',
IDP_PUBLIC_URL_PREFIX: 'http://localhost:3080/realms/veritable/protocol/openid-connect',
INVITATION_FROM_COMPANY_NUMBER: '123456789',
INVITATION_FROM_COMPANY_NUMBER: '04659351',
INVITATION_PIN_SECRET: 'secret',
ISSUANCE_CRED_DEF_POLICY: 'CREATE_NEW',
ISSUANCE_DID_POLICY: 'CREATE_NEW',
Expand Down
31 changes: 24 additions & 7 deletions src/models/emailServiceInt/templates/invitationAdmin.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,40 @@
import Html from '@kitajs/html'
import { SendMailOptions } from 'nodemailer'

import { Env } from '../../../env/index.js'
import CompanyHouseEntity from '../../companyHouseEntity.js'

export default {
name: 'connection_invite_admin' as const,
template: async function (env: Env, params: { pin: string; address: string }): Promise<SendMailOptions> {
template: async function (
env: Env,
params: { pin: string; invitator: string; address: string }
): Promise<SendMailOptions> {
const companyHouse = new CompanyHouseEntity(env)
const localCompany = await companyHouse.localCompanyHouseProfile()
return {
to: env.get('EMAIL_ADMIN_ADDRESS'),
from: env.get('EMAIL_FROM_ADDRESS'),
subject: 'Action required: process veritable invitation',
subject: `Postal Code for Verification: Invitation from ${params.invitator} on Veritable`,
text: `
Action required: process veritable invitation
PIN: ${params.pin},
Address: ${params.address},
Hi ${localCompany.company_name},
${params.invitator} has sent you a request to connect securely on Veritable. To complete the verification process, please enter the following 6-digit postal code within your Veritable instance.
Verification Code: ${params.pin},
Address: ${params.address}
`,
html: await (
<>
<h1>Action required: process veritable invitation</h1>
<p>Pin:</p>
<h1>{Html.escapeHtml(`Hi ${localCompany.company_name}`)}</h1>
<br />
<p>
{Html.escapeHtml(
`${params.invitator} has sent you a request to connect securely on Veritable. To complete the verification process, please enter the following 6-digit postal code within your Veritable instance.`
)}
</p>

<br />
<p>Verification Code:</p>
<p>{Html.escapeHtml(params.pin)}</p>
<p>Address:</p>
<p>{Html.escapeHtml(params.address)}</p>
Expand Down
12 changes: 0 additions & 12 deletions test/e2e/a-homepage-redirect.spec.ts

This file was deleted.

40 changes: 40 additions & 0 deletions test/e2e/a-homepage.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { expect, Page, test } from '@playwright/test'
import { cleanup, CustomBrowserContext, withLoggedInUser, withRegisteredAccount } from './helpers/registerLogIn'

test.describe('Homepage and homepage related tests', () => {
const baseKeycloakUrl = process.env.VERITABLE_KEYCLOAK_URL_PREFIX || 'http://localhost:3080'
const keycloakUrl = `${baseKeycloakUrl}/realms/veritable/protocol/openid-connect/auth?response_type=code&client_id=veritable-ui&redirect_uri=http`
const baseUrl = process.env.VERITABLE_ALICE_PUBLIC_URL || 'http://localhost:3000'
let page: Page
let context: CustomBrowserContext

test.beforeEach(async ({ browser }) => {
context = await browser.newContext()
page = await context.newPage()
})

test.afterAll(async () => {
await cleanup([baseUrl])
await page.close()
})

test('redirects to keycloak', async () => {
await page.goto(baseUrl)

const url = page.url()
expect(url).toContain(keycloakUrl)

await expect(page).toHaveScreenshot({ maxDiffPixelRatio: 0.03 })
})

test.describe('after successful login', async () => {
test.beforeEach(async () => {
await withRegisteredAccount(page, context, baseUrl)
await withLoggedInUser(page, context, baseUrl)
})

test('renders a home page', async () => {
await expect(page).toHaveScreenshot({ maxDiffPixelRatio: 0.03 })
})
})
})
6 changes: 2 additions & 4 deletions test/e2e/connection-alice-to-bob.spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { expect, Page, test } from '@playwright/test'
import { cleanup, CustomBrowserContext, withLoggedInUser, withRegisteredAccount } from './helpers/registerLogIn.js'
import { checkEmails, extractInvite, extractPin, findNewAdminEmail } from './helpers/smtpEmails.js'
import { checkEmails, extractInvite, extractPin } from './helpers/smtpEmails.js'

test.describe('Connection from Alice to Bob', () => {
let context: CustomBrowserContext
let page: Page
let adminEmailId: string
let invite: string | null
let pinForBob: string
let pinForAlice: string
Expand Down Expand Up @@ -73,7 +72,6 @@ test.describe('Connection from Alice to Bob', () => {
await test.step('Retrieve invite and pin for Bob', async () => {
const adminEmail = await checkEmails('[email protected]')
const inviteEmail = await checkEmails('[email protected]')
adminEmailId = adminEmail.id

const extractedPin = await extractPin(adminEmail.id)
expect(extractedPin).toHaveLength(6)
Expand Down Expand Up @@ -123,7 +121,7 @@ test.describe('Connection from Alice to Bob', () => {
})

await test.step('Retrieve pin for Alice', async () => {
const newAdminEmail = await findNewAdminEmail(adminEmailId)
const newAdminEmail = await checkEmails('[email protected]')
const extractedPin = await extractPin(newAdminEmail.id)
expect(extractedPin).toHaveLength(6)
if (!extractedPin) throw new Error('PIN from admin email was not found.')
Expand Down
2 changes: 1 addition & 1 deletion test/e2e/helpers/setupConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export async function withConnection(invitatorUrl: string, receiverUrl: string)
stepCount: '2',
})

retries = 20
retries = 30
while (retries) {
await delay(500)
const invitatorConnection = await fetchGet(`${invitatorUrl}/connection?search=OFFSHORE`)
Expand Down
9 changes: 4 additions & 5 deletions test/e2e/helpers/smtpEmails.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,11 @@ async function extractPin(emailId: string): Promise<string | null> {

const rawEmailContent = await response.text()
// Regex pattern to find the PIN
const pinPattern = /<p>Pin:<\/p><p>(\d{6})<\/p>/
const pinPattern = /\d{6}/g
const match = rawEmailContent.match(pinPattern)

if (match && match[1]) {
return match[1]
if (match && match[0]) {
return match[0]
} else {
return null
}
Expand Down Expand Up @@ -136,8 +136,7 @@ async function findNewAdminEmail(oldAdminEmailId: string): Promise<Email> {
}

const newAdminEmail = results.find(
(msg: Email) =>
msg.subject === 'Action required: process veritable invitation' && msg.id !== oldAdminEmailId
(msg: Email) => msg.subject === 'Postal Code for Verification:' && msg.id !== oldAdminEmailId
)

if (!newAdminEmail) {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 25 additions & 5 deletions test/integration/smtpEmail.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ describe('SMTP email', () => {
await cleanupCloudagent()
server.cloudagentEvents.stop()
})

it('should send an email via SMTP', async () => {
const options = {
hostname: 'localhost',
Expand All @@ -75,26 +76,45 @@ describe('SMTP email', () => {
const parsedMessages = EmailResponseSchema.parse(messages)
const results = parsedMessages['results']
expect(results).to.be.an('array')
expect(results).length(2)
expect(results[0]).to.have.property('deliveredTo').that.is.equal('[email protected]')
expect(results[1]).to.have.property('deliveredTo').that.is.equal('[email protected]')

// Invite email assertions
const inviteEmail = results.find(
(msg) => msg.subject === `${validCompanyName} invites you to a secure, verified connection on Veritable`
)
if (inviteEmail) {
expect(inviteEmail.to).to.have.lengthOf(1)
expect(inviteEmail.to[0]).to.contain('[email protected]')
expect(inviteEmail).to.deep.contain({
isRelayed: false,
deliveredTo: '[email protected]',
from: '[email protected]',
to: ['[email protected]'],
subject: 'DIGITAL CATAPULT invites you to a secure, verified connection on Veritable',
attachmentCount: 0,
isUnread: true,
})
} else {
throw new Error('No email found with the correct subject.')
}

// Admin email assertions
const adminEmail = results.find((msg) => msg.subject === 'Action required: process veritable invitation')
const adminEmail = results.find(
(msg) => msg.subject === 'Postal Code for Verification: Invitation from DIGITAL CATAPULT on Veritable'
)
if (adminEmail) {
expect(adminEmail.to).to.have.lengthOf(1)
expect(adminEmail.to[0]).to.contain('[email protected]')
expect(adminEmail).to.deep.contain({
isRelayed: false,
deliveredTo: '[email protected]',
from: '[email protected]',
to: ['[email protected]'],
subject: 'Postal Code for Verification: Invitation from DIGITAL CATAPULT on Veritable',
attachmentCount: 0,
isUnread: true,
})
} else {
throw new Error('No email found with the subject "Action required: process veritable invitation".')
throw new Error('No email found with the subject "Postal Code for Verification".')
}

resolve(true)
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 12da48a

Please sign in to comment.