diff --git a/e2e/fixtures/index.html b/e2e/fixtures/index.html
new file mode 100644
index 00000000..66acd60c
--- /dev/null
+++ b/e2e/fixtures/index.html
@@ -0,0 +1,10 @@
+
+
+
+
+ Hello World
+
+
+ Hello World
+
+
diff --git a/e2e/page-objects/cli.page.ts b/e2e/page-objects/cli.page.ts
index 13cb4957..4ce962db 100644
--- a/e2e/page-objects/cli.page.ts
+++ b/e2e/page-objects/cli.page.ts
@@ -1,8 +1,8 @@
import {assertNonNullish, notEmptyString} from '@dfinity/utils';
import type {PrincipalText} from '@dfinity/zod-schemas';
import {execute, spawn} from '@junobuild/cli-tools';
-import {readdirSync, statSync} from 'node:fs';
-import {readFile, writeFile} from 'node:fs/promises';
+import {statSync} from 'node:fs';
+import {readdir, readFile, writeFile} from 'node:fs/promises';
import {join} from 'node:path';
import {TestPage} from './_page';
@@ -23,7 +23,7 @@ export interface CliPageParams {
}
export class CliPage extends TestPage {
- readonly #satelliteId: PrincipalText;
+ #satelliteId: PrincipalText;
private constructor({satelliteId}: CliPageParams) {
super();
@@ -55,6 +55,12 @@ export class CliPage extends TestPage {
await writeFile(JUNO_CONFIG, content, 'utf-8');
}
+ async toggleSatelliteId({satelliteId}: {satelliteId: PrincipalText}): Promise {
+ await this.revertConfig();
+ this.#satelliteId = satelliteId;
+ await this.initConfig();
+ }
+
protected async loginWithEmulator(): Promise {
await execute({
command: JUNO_CMD,
@@ -83,6 +89,13 @@ export class CliPage extends TestPage {
});
}
+ async deployHosting({clear}: {clear: boolean}): Promise {
+ await execute({
+ command: JUNO_CMD,
+ args: buildArgs(['hosting', 'deploy', ...(clear ? ['--clear'] : [])])
+ });
+ }
+
async createSnapshot({
target
}: {
@@ -126,9 +139,16 @@ export class CliPage extends TestPage {
args: buildArgs(['snapshot', 'download', '--target', target])
});
- // Retrieve where the snapshot was created
+ return await this.getSnapshotFsFolder();
+ }
+
+ // Retrieve where the snapshot was created
+ async getSnapshotFsFolder(): Promise<{snapshotFolder: string}> {
const snapshotsFolder = join(process.cwd(), '.snapshots');
- const [snapshotFolder] = readdirSync(snapshotsFolder, {withFileTypes: true})
+
+ const folders = await readdir(snapshotsFolder, {withFileTypes: true});
+
+ const [snapshotFolder] = folders
.filter((d) => d.isDirectory())
.map(({name}) => {
const path = join(snapshotsFolder, name);
@@ -173,6 +193,23 @@ export class CliPage extends TestPage {
return {snapshotId: notEmptyString(snapshotId) ? snapshotId.trim() : undefined};
}
+ async whoami(): Promise<{accessKey: string}> {
+ let output = '';
+
+ await spawn({
+ command: JUNO_CMD,
+ args: buildArgs(['whoami']),
+ stdout: (o) => (output += o),
+ silentErrors: true
+ });
+
+ const [_, __, ___, text] = output.split(' ');
+ const [value] = text.split('\n');
+ const accessKey = value.replace('\x1B[32m', '').replace('\x1B[39m', '');
+
+ return {accessKey: accessKey.trim()};
+ }
+
/**
* @override
*/
diff --git a/e2e/page-objects/console.page.ts b/e2e/page-objects/console.page.ts
index 326abf1f..bd077260 100644
--- a/e2e/page-objects/console.page.ts
+++ b/e2e/page-objects/console.page.ts
@@ -1,6 +1,6 @@
import {InternetIdentityPage} from '@dfinity/internet-identity-playwright';
import {notEmptyString} from '@dfinity/utils';
-import {PrincipalTextSchema} from '@dfinity/zod-schemas';
+import {PrincipalText, PrincipalTextSchema} from '@dfinity/zod-schemas';
import {expect} from '@playwright/test';
import {testIds} from '../constants/test-ids.constants';
import {IdentityPage, type IdentityPageParams} from './identity.page';
@@ -31,8 +31,8 @@ export class ConsolePage extends IdentityPage {
return consolePage;
}
- async goto(): Promise {
- await this.page.goto('/');
+ async goto({path}: {path: string} = {path: '/'}): Promise {
+ await this.page.goto(path);
}
async signIn(): Promise {
@@ -49,11 +49,15 @@ export class ConsolePage extends IdentityPage {
}
async createSatellite({kind}: {kind: 'website' | 'application'}): Promise {
- await expect(this.page.getByTestId(testIds.createSatellite.launch)).toBeVisible();
+ await expect(this.page.getByTestId(testIds.createSatellite.launch)).toBeVisible({
+ timeout: 20000
+ });
await this.page.getByTestId(testIds.createSatellite.launch).click();
- await expect(this.page.getByTestId(testIds.createSatellite.create)).toBeVisible();
+ await expect(this.page.getByTestId(testIds.createSatellite.create)).toBeVisible({
+ timeout: 15000
+ });
await this.page.getByTestId(testIds.createSatellite.input).fill('Test');
await this.page.getByTestId(testIds.createSatellite[kind]).click();
@@ -70,7 +74,9 @@ export class ConsolePage extends IdentityPage {
async visitSatelliteSite(
{title}: {title: string} = {title: 'Juno / Satellite'}
): Promise {
- await expect(this.page.getByTestId(testIds.satelliteOverview.visit)).toBeVisible();
+ await expect(this.page.getByTestId(testIds.satelliteOverview.visit)).toBeVisible({
+ timeout: 20000
+ });
const satellitePagePromise = this.context.waitForEvent('page');
@@ -87,6 +93,18 @@ export class ConsolePage extends IdentityPage {
});
}
+ async getICP(): Promise {
+ await expect(this.page.getByTestId(testIds.navbar.openWallet)).toBeVisible();
+
+ await this.page.getByTestId(testIds.navbar.openWallet).click();
+
+ await expect(this.page.getByTestId(testIds.navbar.getIcp)).toBeVisible();
+
+ await this.page.getByTestId(testIds.navbar.getIcp).click();
+
+ await expect(this.page.getByText('55.0001')).toBeVisible({timeout: 65000});
+ }
+
async copySatelliteID(): Promise {
await expect(this.page.getByTestId(testIds.satelliteOverview.copySatelliteId)).toBeVisible();
@@ -99,4 +117,34 @@ export class ConsolePage extends IdentityPage {
return satelliteId;
}
+
+ async addSatelliteAdminAccessKey({
+ satelliteId,
+ accessKey
+ }: {
+ satelliteId: PrincipalText;
+ accessKey: string;
+ }): Promise {
+ await this.goto({path: `/satellite/?s=${satelliteId}&tab=setup`});
+
+ const btnLocator = this.page.locator('button', {hasText: 'Add an access key'});
+ await expect(btnLocator).toBeVisible({timeout: 10000});
+ await btnLocator.click();
+
+ const form = this.page.locator('form');
+
+ await form.getByRole('radio', {name: /enter one manually/i}).check();
+
+ const keyField = form.getByLabel('Access Key ID');
+ await expect(keyField).toBeEnabled();
+ await keyField.fill(accessKey);
+
+ await form.locator('select[name="scope"]').selectOption('admin');
+
+ const submitLocator = form.getByRole('button', {name: /^submit$/i});
+ await expect(submitLocator).toBeEnabled();
+ await submitLocator.click();
+
+ await expect(this.page.getByText('Access Key Added')).toBeVisible({timeout: 10000});
+ }
}
diff --git a/e2e/snapshots.spec.ts b/e2e/snapshots.spec.ts
index 04cd7517..84126a70 100644
--- a/e2e/snapshots.spec.ts
+++ b/e2e/snapshots.spec.ts
@@ -25,7 +25,7 @@ testWithII('should create and restore a snapshot', async () => {
});
testWithII('should create, download, delete, upload and restore a snapshot', async () => {
- testWithII.slow();
+ testWithII.setTimeout(120_000);
const {consolePage, cliPage} = getTestPages();
@@ -52,3 +52,44 @@ testWithII('should create, download, delete, upload and restore a snapshot', asy
await satellitePage.reload();
await satellitePage.assertScreenshot();
});
+
+testWithII(
+ 'should create, download, delete, upload and restore a snapshot to another satellite',
+ async () => {
+ testWithII.setTimeout(120_000);
+
+ const {consolePage, cliPage} = getTestPages();
+
+ await consolePage.getICP();
+
+ await consolePage.goto();
+
+ await consolePage.createSatellite({kind: 'application'});
+
+ const satelliteId = await consolePage.copySatelliteID();
+
+ await cliPage.toggleSatelliteId({satelliteId});
+
+ const {accessKey} = await cliPage.whoami();
+
+ await consolePage.addSatelliteAdminAccessKey({accessKey, satelliteId});
+
+ await cliPage.deployHosting({clear: true});
+
+ await consolePage.goto({path: `/satellite/?s=${satelliteId}`});
+
+ const satellitePage = await consolePage.visitSatelliteSite({
+ title: 'Hello World'
+ });
+ await satellitePage.assertScreenshot();
+
+ const {snapshotFolder} = await cliPage.getSnapshotFsFolder();
+
+ await cliPage.uploadSnapshot({...SNAPSHOT_TARGET, folder: snapshotFolder});
+
+ await cliPage.restoreSnapshot(SNAPSHOT_TARGET);
+
+ await satellitePage.reload();
+ await satellitePage.assertScreenshot();
+ }
+);
diff --git a/e2e/snapshots/snapshots.spec.ts-snapshots/should-create-download-delete-upload-and-restore-a-snapshot-to-another-satellite-1-chromium-linux.png b/e2e/snapshots/snapshots.spec.ts-snapshots/should-create-download-delete-upload-and-restore-a-snapshot-to-another-satellite-1-chromium-linux.png
new file mode 100644
index 00000000..4fbe70d0
Binary files /dev/null and b/e2e/snapshots/snapshots.spec.ts-snapshots/should-create-download-delete-upload-and-restore-a-snapshot-to-another-satellite-1-chromium-linux.png differ
diff --git a/e2e/snapshots/snapshots.spec.ts-snapshots/should-create-download-delete-upload-and-restore-a-snapshot-to-another-satellite-2-chromium-linux.png b/e2e/snapshots/snapshots.spec.ts-snapshots/should-create-download-delete-upload-and-restore-a-snapshot-to-another-satellite-2-chromium-linux.png
new file mode 100644
index 00000000..7b6610c0
Binary files /dev/null and b/e2e/snapshots/snapshots.spec.ts-snapshots/should-create-download-delete-upload-and-restore-a-snapshot-to-another-satellite-2-chromium-linux.png differ
diff --git a/juno.config.ts b/juno.config.ts
index 90cddcb0..caa0213c 100644
--- a/juno.config.ts
+++ b/juno.config.ts
@@ -6,8 +6,8 @@ export default defineConfig({
development: '',
production: ''
},
- source: 'build',
- predeploy: ['npm run build'],
+ source: 'e2e/fixtures',
+ precompress: false,
collections: {
datastore: [
{