From cab2a549521b95a7434d807417693e5f8de92f45 Mon Sep 17 00:00:00 2001 From: Developer Date: Fri, 18 Oct 2024 16:28:43 +0200 Subject: [PATCH] ONE-2521: mDoc design e2e test --- e2e/helpers/credentialSchemas.ts | 212 ++++++++++++++++++ e2e/page-objects/components/CredentialCard.ts | 36 ++- e2e/tests/credential-design.e2e.ts | 120 +++++++++- e2e/utils/bff-api.ts | 2 +- e2e/utils/enums.ts | 1 + e2e/utils/{query-params.ts => utils.ts} | 6 + 6 files changed, 365 insertions(+), 12 deletions(-) create mode 100644 e2e/helpers/credentialSchemas.ts rename e2e/utils/{query-params.ts => utils.ts} (84%) diff --git a/e2e/helpers/credentialSchemas.ts b/e2e/helpers/credentialSchemas.ts new file mode 100644 index 00000000..bb5371a0 --- /dev/null +++ b/e2e/helpers/credentialSchemas.ts @@ -0,0 +1,212 @@ +import { CredentialSchemaResponseDTO } from '../types/credential'; +import { CredentialSchemaData } from '../types/credentialSchema'; +import { createCredentialSchema } from '../utils/bff-api'; +import { + CodeType, + CredentialFormat, + DataType, + LayoutType, + RevocationMethod, +} from '../utils/enums'; +import { shortUUID } from '../utils/utils'; + +export const mDocCredentialSchema = async (authToken: string) => { + const uuid = shortUUID(); + const claims = [ + { + array: false, + claims: [ + { + array: false, + datatype: DataType.STRING, + key: 'country', + required: true, + }, + { + array: false, + datatype: DataType.STRING, + key: 'region', + required: true, + }, + { + array: false, + datatype: DataType.STRING, + key: 'city', + required: true, + }, + { + array: false, + datatype: DataType.STRING, + key: 'street', + required: true, + }, + ], + datatype: DataType.OBJECT, + key: 'Address', + required: true, + }, + { + array: false, + claims: [ + { + array: false, + datatype: DataType.STRING, + key: 'first name', + required: true, + }, + { + array: false, + datatype: DataType.STRING, + key: 'last name', + required: true, + }, + { + array: false, + datatype: DataType.BIRTH_DATE, + key: 'Birthday', + required: true, + }, + { + array: false, + datatype: DataType.MDL_PICTURE, + key: 'image', + required: true, + }, + ], + datatype: DataType.OBJECT, + key: 'Credentials', + required: true, + }, + { + array: false, + claims: [ + { + array: true, + claims: [ + { + array: false, + datatype: DataType.STRING, + key: 'Category', + required: true, + }, + { + array: false, + datatype: DataType.DATE, + key: 'Expired', + required: true, + }, + ], + datatype: DataType.OBJECT, + key: 'Categories', + required: true, + }, + ], + datatype: DataType.OBJECT, + key: 'Data', + required: true, + }, + ]; + const data: CredentialSchemaData = { + claims: claims, + format: CredentialFormat.MDOC, + layoutProperties: { + background: { + color: '#375548', + }, + code: { + attribute: 'Address/city', + type: CodeType.QrCode, + }, + logo: { + backgroundColor: '#77722a', + fontColor: '#b23d3d', + }, + pictureAttribute: 'Credentials/image', + primaryAttribute: 'Credentials/first name', + secondaryAttribute: 'Credentials/last name', + }, + layoutType: LayoutType.CARD, + name: `Driver's license ${uuid}`, + revocationMethod: RevocationMethod.NONE, + schemaId: `org.iso.18013.5.1.mDL-${uuid}`, + }; + return await createCredentialSchema(authToken, data, false); +}; + +export const mDocCredentialClaims = ( + mdocSchema: CredentialSchemaResponseDTO, +) => { + return [ + { + claimId: mdocSchema.claims![2].claims![0].claims![0].id, + path: 'Data/Categories/0/Category', + value: 'A', + }, + { + claimId: mdocSchema.claims![2].claims![0].claims![1].id, + path: 'Data/Categories/0/Expired', + value: '2026-09-29T00:00:00.000Z', + }, + { + claimId: mdocSchema.claims![2].claims![0].claims![0].id, + path: 'Data/Categories/1/Category', + value: 'B', + }, + { + claimId: mdocSchema.claims![2].claims![0].claims![1].id, + path: 'Data/Categories/1/Expired', + value: '2030-09-30T00:00:00.000Z', + }, + { + claimId: mdocSchema.claims![2].claims![0].claims![0].id, + path: 'Data/Categories/2/Category', + value: 'C', + }, + { + claimId: mdocSchema.claims![2].claims![0].claims![1].id, + path: 'Data/Categories/2/Expired', + value: '2027-09-28T00:00:00.000Z', + }, + { + claimId: mdocSchema.claims![0].claims![0].id, + path: 'Address/country', + value: 'CH', + }, + { + claimId: mdocSchema.claims![0].claims![1].id, + path: 'Address/region', + value: 'Zurich', + }, + { + claimId: mdocSchema.claims![0].claims![2].id, + path: 'Address/city', + value: 'Zurich', + }, + { + claimId: mdocSchema.claims![0].claims![3].id, + path: 'Address/street', + value: 'strasse', + }, + { + claimId: mdocSchema.claims![1].claims![0].id, + path: 'Credentials/first name', + value: 'Wade', + }, + { + claimId: mdocSchema.claims![1].claims![1].id, + path: 'Credentials/last name', + value: 'Wilson', + }, + { + claimId: mdocSchema.claims![1].claims![2].id, + path: 'Credentials/Birthday', + value: '2018-01-17T00:00:00.000Z', + }, + { + claimId: mdocSchema.claims![1].claims![3].id, + path: 'Credentials/image', + value: + 'data:image/jpeg;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==', + }, + ]; +}; diff --git a/e2e/page-objects/components/CredentialCard.ts b/e2e/page-objects/components/CredentialCard.ts index fd87d439..a109042c 100644 --- a/e2e/page-objects/components/CredentialCard.ts +++ b/e2e/page-objects/components/CredentialCard.ts @@ -147,14 +147,42 @@ export default function CredentialCard(testID: string) { await this.verifyCarouselIsVisible(); await this.body.carousel.element.swipe(direction); }, - verifyAttributeValue: async function (key: string, value: string) { - await expect(this.attribute(key).value).toHaveText(value); + verifyAttributeValue: async function ( + index: string, + title: string, + value?: string, + image?: boolean, + ) { + await expect(this.attribute(index).title).toHaveText(title); + if (image) { + await expect(this.attribute(index).image).toBeVisible(); + } else if (value) { + await expect(this.attribute(index).value).toHaveText(value); + } else { + throw new Error('Attribute value or image must be provided'); + } }, verifyAttributeValues: async function ( - attributes: Array<{ key: string; value: string }>, + attributes: Array<{ + image?: boolean; + index: string; + key: string; + value?: string; + }>, + scrollTo?: ( + element: Detox.IndexableNativeElement, + direction: 'up' | 'down', + startPositionY: number, + ) => Promise, ) { for (const attribute of attributes) { - await this.verifyAttributeValue(attribute.key, attribute.value); + await scrollTo?.(this.attribute(attribute.index).element, 'down', 0.5); + await this.verifyAttributeValue( + attribute.index, + attribute.key, + attribute.value, + attribute.image, + ); } }, verifyCardBackgroundColor: async function (backgroundColor: string) { diff --git a/e2e/tests/credential-design.e2e.ts b/e2e/tests/credential-design.e2e.ts index c657c42c..93600c28 100644 --- a/e2e/tests/credential-design.e2e.ts +++ b/e2e/tests/credential-design.e2e.ts @@ -2,6 +2,10 @@ import { expect } from 'detox'; import { v4 as uuidv4 } from 'uuid'; import { credentialIssuance } from '../helpers/credential'; +import { + mDocCredentialClaims, + mDocCredentialSchema, +} from '../helpers/credentialSchemas'; import { CarouselImageType } from '../page-objects/components/CredentialCard'; import CredentialHistoryScreen from '../page-objects/credential/CredentialHistoryScreen'; import CredentialNerdScreen, { @@ -26,6 +30,7 @@ import { DataType, DidMethod, Exchange, + KeyType, RevocationMethod, } from '../utils/enums'; import { launchApp, reloadApp } from '../utils/init'; @@ -123,6 +128,7 @@ describe('ONE-2014: Credential design', () => { }, [AttributeTestID.documentType]: { label: 'Document type', + showMoreButton: true, value: credentialDetail.schema.schemaId, }, [AttributeTestID.revocationMethod]: { @@ -486,7 +492,7 @@ describe('ONE-2014: Credential design', () => { }); }); - // Fail + // Pass describe('ONE-1893: Credential Schema Layout', () => { let schema1: CredentialSchemaResponseDTO; @@ -551,7 +557,7 @@ describe('ONE-2014: Credential design', () => { { claimId: schema1.claims[2].id, path: schema1.claims[2].key, - value: '1984-10-26T21:00:00.000Z', + value: '1984-10-26T00:00:00.000Z', }, { claimId: schema1.claims[3].id, @@ -582,11 +588,15 @@ describe('ONE-2014: Credential design', () => { it('Test credential card attributes', async () => { await CredentialDetailScreen.credentialCard.showAllAttributes(); - await CredentialDetailScreen.credentialCard.verifyAttributeValues([ - { key: 'first name', value: 'John' }, - { key: 'Last name', value: 'Connor' }, - { key: 'Birthday', value: '10/26/1984' }, - ]); + await CredentialDetailScreen.credentialCard.verifyAttributeValues( + [ + { index: '0', key: 'first name', value: 'John' }, + { index: '1', key: 'Last name', value: 'Connor' }, + { index: '2', key: 'Birthday', value: '10/26/1984' }, + { image: true, index: '3', key: 'Photo' }, + ], + CredentialDetailScreen.scrollTo, + ); await CredentialDetailScreen.scrollTo( CredentialDetailScreen.credentialCard.header.name, @@ -616,4 +626,100 @@ describe('ONE-2014: Credential design', () => { ); }); }); + + describe('ONE-2395: Credential Layout mDoc', () => { + let mdocSchema: CredentialSchemaResponseDTO; + + beforeAll(async () => { + await launchApp({ delete: true }); + mdocSchema = await mDocCredentialSchema(authToken); + await credentialIssuance({ + authToken: authToken, + claimValues: mDocCredentialClaims(mdocSchema), + credentialSchema: mdocSchema, + didMethods: DidMethod.MDL, + exchange: Exchange.OPENID4VC, + keyAlgorithms: KeyType.ES256, + }); + await WalletScreen.openDetailScreen(mdocSchema.name); + }); + + it('Test credential card header', async () => { + await expect(CredentialDetailScreen.screen).toBeVisible(); + await expect(CredentialDetailScreen.credentialCard.element).toBeVisible(); + + await CredentialDetailScreen.credentialCard.verifyCredentialName( + mdocSchema.name, + ); + await CredentialDetailScreen.credentialCard.verifyDetailLabel( + 'Wade', + 'Wilson', + ); + }); + + it('Test collapse & expand card', async () => { + await CredentialDetailScreen.credentialCard.collapseOrExpand(); + await CredentialDetailScreen.credentialCard.verifyIsCardCollapsed(); + await CredentialDetailScreen.credentialCard.collapseOrExpand(); + await CredentialDetailScreen.credentialCard.verifyIsCardCollapsed(false); + }); + + it('Test credential carousel', async () => { + await CredentialDetailScreen.credentialCard.verifyImageIsVisible( + CarouselImageType.Photo, + ); + await CredentialDetailScreen.credentialCard.verifyImageIsVisible( + CarouselImageType.QrCode, + false, + ); + + await CredentialDetailScreen.credentialCard.swipe('right'); + + await CredentialDetailScreen.credentialCard.verifyImageIsVisible( + CarouselImageType.QrCode, + ); + await CredentialDetailScreen.credentialCard.verifyImageIsVisible( + CarouselImageType.Photo, + false, + ); + }); + + it('Test credential background color', async () => { + await CredentialDetailScreen.credentialCard.verifyCardBackgroundColor( + // eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain + mdocSchema.layoutProperties.background?.color!, + ); + }); + + it('Test logo background colors', async () => { + await CredentialDetailScreen.credentialCard.verifyLogoColor( + // eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain + mdocSchema.layoutProperties.logo?.backgroundColor!, + // eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain + mdocSchema.layoutProperties.logo?.fontColor!, + ); + }); + + it('Test credential card attributes', async () => { + await CredentialDetailScreen.credentialCard.verifyAttributeValues( + [ + { index: '0.0', key: 'country', value: 'CH' }, + { index: '0.1', key: 'region', value: 'Zurich' }, + { index: '0.2', key: 'city', value: 'Zurich' }, + { index: '0.3', key: 'street', value: 'strasse' }, + { index: '1.0', key: 'first name', value: 'Wade' }, + { index: '1.1', key: 'last name', value: 'Wilson' }, + { index: '1.2', key: 'Birthday', value: '1/17/2018' }, + { image: true, index: '1.3', key: 'image' }, + { index: '2.0.0.0', key: 'Category', value: 'A' }, + { index: '2.0.0.1', key: 'Expired', value: '9/29/2026' }, + { index: '2.0.1.0', key: 'Category', value: 'B' }, + { index: '2.0.1.1', key: 'Expired', value: '9/30/2030' }, + { index: '2.0.2.0', key: 'Category', value: 'C' }, + { index: '2.0.2.1', key: 'Expired', value: '9/28/2027' }, + ], + CredentialDetailScreen.scrollTo, + ); + }); + }); }); diff --git a/e2e/utils/bff-api.ts b/e2e/utils/bff-api.ts index 1b03f92c..e7ce08be 100644 --- a/e2e/utils/bff-api.ts +++ b/e2e/utils/bff-api.ts @@ -24,7 +24,7 @@ import { LayoutType, RevocationMethod, } from './enums'; -import { objectToQueryParams } from './query-params'; +import { objectToQueryParams } from './utils'; const BFF_BASE_URL = 'https://desk.dev.procivis-one.com'; const LOGIN = { diff --git a/e2e/utils/enums.ts b/e2e/utils/enums.ts index 490c3ad6..b4ffceee 100644 --- a/e2e/utils/enums.ts +++ b/e2e/utils/enums.ts @@ -50,6 +50,7 @@ export enum CredentialFormat { JSON_LD_CLASSIC = 'JSON_LD_CLASSIC', JWT = 'JWT', MDOC = 'MDOC', + PHYSICAL_CARD = 'PHYSICAL_CARD', SDJWT = 'SDJWT', } diff --git a/e2e/utils/query-params.ts b/e2e/utils/utils.ts similarity index 84% rename from e2e/utils/query-params.ts rename to e2e/utils/utils.ts index 71a35091..3de31810 100644 --- a/e2e/utils/query-params.ts +++ b/e2e/utils/utils.ts @@ -1,3 +1,5 @@ +import { v4 as uuidv4 } from 'uuid'; + export const objectToQueryParams = (obj: Record): string => { const params = new URLSearchParams(); @@ -20,3 +22,7 @@ export const objectToQueryParams = (obj: Record): string => { return params.toString(); }; + +export const shortUUID = () => { + return uuidv4().split('-')[0]; +};