Skip to content

Commit

Permalink
Add verifiable credentials e2e tests
Browse files Browse the repository at this point in the history
  • Loading branch information
nmattia committed Dec 4, 2023
1 parent f4c8c47 commit 9fb6307
Show file tree
Hide file tree
Showing 3 changed files with 194 additions and 2 deletions.
1 change: 1 addition & 0 deletions src/frontend/src/test-e2e/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const TEST_APP_CANONICAL_URL_LEGACY = `https://${testAppCanisterId}.ic0.a
export const TEST_APP_NICE_URL = "https://nice-name.com";

export const ISSUER_APP_URL = `https://${issuerAppCanisterId}.icp0.io`;
export const ISSUER_APP_URL_LEGACY = `https://${issuerAppCanisterId}.ic0.app`;

export const II_URL =
process.env.II_URL ?? "https://identity.internetcomputer.org";
Expand Down
114 changes: 112 additions & 2 deletions src/frontend/src/test-e2e/verifiableCredentials.test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,27 @@
import { FLOWS } from "./flows";
import {
addVirtualAuthenticator,
addWebAuthnCredential,
getWebAuthnCredentials,
originToRelyingPartyId,
runInBrowser,
switchToPopup,
waitToClose,
} from "./util";
import { IssuerAppView } from "./views";
import {
AuthenticateView,
IssuerAppView,
VcAllowView,
VcTestAppView,
} from "./views";

import { II_URL, ISSUER_APP_URL } from "./constants";
import {
II_URL,
ISSUER_APP_URL,
ISSUER_APP_URL_LEGACY,
TEST_APP_CANONICAL_URL,
TEST_APP_CANONICAL_URL_LEGACY,
} from "./constants";

test("Can add employee on issuer app", async () => {
await runInBrowser(async (browser: WebdriverIO.Browser) => {
Expand All @@ -26,3 +40,99 @@ test("Can add employee on issuer app", async () => {
expect(msg).toContain(principal);
});
}, 300_000);

const getDomain = (url: string) => url.split(".").slice(1).join(".");

[TEST_APP_CANONICAL_URL, TEST_APP_CANONICAL_URL_LEGACY].forEach(
(relyingParty) =>
[ISSUER_APP_URL, ISSUER_APP_URL_LEGACY].forEach((issuer) => {
const testSuffix = `RP: ${getDomain(relyingParty)}, ISS: ${getDomain(
issuer
)}`;

test(
"Can issue credentials " + testSuffix,
async () => {
await runInBrowser(async (browser: WebdriverIO.Browser) => {
const authenticatorId_register = await addVirtualAuthenticator(
browser
);
await browser.url(II_URL);
const userNumber = await FLOWS.registerNewIdentityWelcomeView(
browser
);
await FLOWS.addRecoveryMechanismSeedPhrase(browser);
const credentials = await getWebAuthnCredentials(
browser,
authenticatorId_register
);
expect(credentials).toHaveLength(1);

// 1. Add employee

const issuerAppView = new IssuerAppView(browser);
await issuerAppView.open({
issuerAppUrl: issuer,
iiUrl: II_URL,
});
await issuerAppView.waitForDisplay();
await issuerAppView.authenticate();

const authenticatorId_addEmployee = await switchToPopup(browser);
await addWebAuthnCredential(
browser,
authenticatorId_addEmployee,
credentials[0],
originToRelyingPartyId(II_URL)
);

const authenticateView = new AuthenticateView(browser);
await authenticateView.waitForDisplay();
await authenticateView.pickAnchor(userNumber);
await waitToClose(browser);

const _principal = await issuerAppView.waitForAuthenticated();
const _msg = await issuerAppView.addEmployee();

// 2. Auth to RP

const vcTestApp = new VcTestAppView(browser);
await vcTestApp.open(relyingParty, II_URL, issuer);

await vcTestApp.startSignIn();

const authenticatorId2 = await switchToPopup(browser);
await addWebAuthnCredential(
browser,
authenticatorId2,
credentials[0],
originToRelyingPartyId(II_URL)
);

const authenticateView2 = new AuthenticateView(browser);
await authenticateView2.waitForDisplay();
await authenticateView2.pickAnchor(userNumber);

await vcTestApp.waitForAuthenticated();
await vcTestApp.startVcFlow();
const authenticatorId3 = await switchToPopup(browser);
await addWebAuthnCredential(
browser,
authenticatorId3,
credentials[0],
originToRelyingPartyId(II_URL)
);

const vcAllow = new VcAllowView(browser);
await vcAllow.waitForDisplay();
await vcAllow.typeUserNumber(userNumber);
await vcAllow.allow();
await waitToClose(browser);

// XXX: We don't verify the presentation (yet)
});
},
300_000
);
})
);
81 changes: 81 additions & 0 deletions src/frontend/src/test-e2e/views.ts
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,23 @@ export class AboutView extends View {
}
}

export class VcAllowView extends View {
async waitForDisplay(): Promise<void> {
await this.browser
.$('[data-page="vc-allow"]')
.waitForDisplayed({ timeout: 10_000 });
}

async allow(): Promise<void> {
await this.browser.$('[data-action="allow"]').click();
}

async typeUserNumber(userNumber: string): Promise<void> {
await this.browser.$('[data-role="anchor-input"]').waitForDisplayed();
await this.browser.$('[data-role="anchor-input"]').setValue(userNumber);
}
}

export class IssuerAppView extends View {
async open({
issuerAppUrl,
Expand Down Expand Up @@ -653,6 +670,60 @@ export class IssuerAppView extends View {
}
}

export class VcTestAppView extends View {
async open(
demoAppUrl: string,
iiUrl: string,
issuerUrl: string
): Promise<void> {
await this.browser.url(demoAppUrl);
await setInputValue(this.browser, '[data-role="ii-url"]', iiUrl);
await setInputValue(this.browser, '[data-role="issuer-url"]', issuerUrl);
}

async startSignIn(): Promise<void> {
await this.browser.$('[data-action="authenticate"]').click();
}

async startVcFlow(): Promise<void> {
await this.browser.$('[data-action="verify-employee"]').click();
}

/** Waits for the authentication to finish, the window to close and the principal to update.
* Returns the principal after the user has been authenticated.
*/
async waitForAuthenticated(): Promise<void> {
// wait for the demo app to close the II window
await waitToClose(this.browser);
// wait for the demo app to update the principal
await this.browser.waitUntil(async () => {
const principal_ = await this.getPrincipal();
const principal = (() => {
try {
return Principal.fromText(principal_).toText();
} catch (e) {
return undefined;
}
})();
console.log("PRINCIPAL", principal);

if (principal === undefined) {
return false;
}

if (principal === Principal.anonymous().toText()) {
return false;
}

return true;
});
}

getPrincipal(): Promise<string> {
return this.browser.$('[data-role="principal"]').getText();
}
}

export class DemoAppView extends View {
replicaUrl: string;

Expand Down Expand Up @@ -832,6 +903,16 @@ export class ErrorView extends View {
}
}

async function setInputValue(
browser: WebdriverIO.Browser,
selector: string,
text: string
): Promise<void> {
const elem = await browser.$(selector);
await elem.clearValue();
await elem.setValue(text);
}

async function fillText(
browser: WebdriverIO.Browser,
id: string,
Expand Down

0 comments on commit 9fb6307

Please sign in to comment.