mirror of
https://github.com/logto-io/logto.git
synced 2025-03-31 22:51:25 -05:00
Merge pull request #3290 from logto-io/gao-add-cloud-ui-tests
refactor: init cloud ui tests
This commit is contained in:
commit
f9d1dc1b12
8 changed files with 146 additions and 28 deletions
62
.github/workflows/integration-test.yml
vendored
62
.github/workflows/integration-test.yml
vendored
|
@ -13,6 +13,11 @@ concurrency:
|
|||
|
||||
jobs:
|
||||
package:
|
||||
# See https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs#expanding-or-adding-matrix-configurations
|
||||
strategy:
|
||||
matrix:
|
||||
env: [oss, cloud]
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
|
@ -21,26 +26,38 @@ jobs:
|
|||
- name: Setup Node and pnpm
|
||||
uses: silverhand-io/actions-node-pnpm-run-steps@v2
|
||||
|
||||
- name: Build
|
||||
run: pnpm -r build
|
||||
|
||||
- name: Package
|
||||
run: ./.scripts/package.sh
|
||||
- name: Build and package
|
||||
if: matrix.env != 'cloud'
|
||||
run: |
|
||||
pnpm -r build
|
||||
./.scripts/package.sh
|
||||
|
||||
- name: Build and package (Cloud)
|
||||
if: matrix.env == 'cloud'
|
||||
run: |
|
||||
pnpm -r build
|
||||
./.scripts/package.sh
|
||||
env:
|
||||
IS_CLOUD: 1
|
||||
CONSOLE_PUBLIC_URL: /
|
||||
ADMIN_ENDPOINT: http://localhost:3002
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: integration-test-${{ github.sha }}
|
||||
name: integration-test-${{ github.sha }}-${{ matrix.env }}
|
||||
path: /tmp/logto.tar.gz
|
||||
retention-days: 3
|
||||
|
||||
run-logto:
|
||||
needs: package
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
test_target: [api, ui]
|
||||
target: [api, ui, ui-cloud]
|
||||
|
||||
needs: package
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
INTEGRATION_TEST: true
|
||||
IS_CLOUD: ${{ contains(matrix.target, 'cloud') && '1' || '0' }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
@ -73,12 +90,16 @@ jobs:
|
|||
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: integration-test-${{ github.sha }}
|
||||
name: integration-test-${{ github.sha }}-${{ contains(matrix.target, 'cloud') && 'cloud' || 'oss' }}
|
||||
|
||||
- name: Extract
|
||||
working-directory: tests
|
||||
run: |
|
||||
npm run cli init -- -p ../logto --db postgres://postgres:postgres@localhost:5432/postgres --du ../logto.tar.gz
|
||||
npm run cli init -- \
|
||||
-p ../logto \
|
||||
--db postgres://postgres:postgres@localhost:5432/postgres \
|
||||
--du ../logto.tar.gz \
|
||||
${{ contains(matrix.target, 'cloud') && '--cloud' || '' }}
|
||||
|
||||
- name: Check and add mock connectors
|
||||
working-directory: tests
|
||||
|
@ -89,8 +110,11 @@ jobs:
|
|||
- name: Run Logto
|
||||
working-directory: logto/
|
||||
run: nohup npm start > nohup.out 2> nohup.err < /dev/null &
|
||||
env:
|
||||
INTEGRATION_TEST: true
|
||||
|
||||
- name: Run Logto Cloud
|
||||
working-directory: logto/
|
||||
if: contains(matrix.target, 'cloud')
|
||||
run: nohup npm run start:cloud > nohup-cloud.out 2> nohup-cloud.err < /dev/null &
|
||||
|
||||
- name: Sleep for 5 seconds
|
||||
run: sleep 5
|
||||
|
@ -101,7 +125,7 @@ jobs:
|
|||
run: |
|
||||
cd tests/packages/integration-tests
|
||||
pnpm build
|
||||
pnpm test:${{ matrix.test_target }}
|
||||
pnpm run test:${{ matrix.target }}
|
||||
|
||||
- name: Show logs
|
||||
working-directory: logto/
|
||||
|
@ -110,3 +134,13 @@ jobs:
|
|||
- name: Show error logs
|
||||
working-directory: logto/
|
||||
run: cat nohup.err
|
||||
|
||||
- name: Show cloud logs
|
||||
working-directory: logto/
|
||||
if: contains(matrix.target, 'cloud')
|
||||
run: cat nohup-cloud.out
|
||||
|
||||
- name: Show cloud error logs
|
||||
working-directory: logto/
|
||||
if: contains(matrix.target, 'cloud')
|
||||
run: cat nohup-cloud.err
|
||||
|
|
|
@ -10,8 +10,12 @@ echo Install production dependencies
|
|||
NODE_ENV=production pnpm i
|
||||
|
||||
echo Prune files
|
||||
# Remove cloud in OSS distributions
|
||||
rm -rf packages/cloud
|
||||
|
||||
if [[ "${IS_CLOUD}" != @(1|true|y|yes|yep|yeah) ]]; then
|
||||
# Remove cloud in OSS distributions
|
||||
rm -rf packages/cloud
|
||||
fi
|
||||
|
||||
# Some node packages use `src` as their dist folder, so ignore them from the rm list in the end
|
||||
find \
|
||||
.git .changeset .changeset-staged .devcontainer .github .husky .parcel-cache .scripts .vscode pnpm-*.yaml *.js \
|
||||
|
|
|
@ -18,11 +18,11 @@ import {
|
|||
export type InstallArgs = {
|
||||
path?: string;
|
||||
skipSeed: boolean;
|
||||
officialConnectors?: boolean;
|
||||
cloud: boolean;
|
||||
downloadUrl?: string;
|
||||
};
|
||||
|
||||
const installLogto = async ({ path, skipSeed, officialConnectors, downloadUrl }: InstallArgs) => {
|
||||
const installLogto = async ({ path, skipSeed, downloadUrl, cloud }: InstallArgs) => {
|
||||
validateNodeVersion();
|
||||
|
||||
// Get instance path
|
||||
|
@ -44,7 +44,7 @@ const installLogto = async ({ path, skipSeed, officialConnectors, downloadUrl }:
|
|||
)} command to seed database when ready.\n`
|
||||
);
|
||||
} else {
|
||||
await seedDatabase(instancePath);
|
||||
await seedDatabase(instancePath, cloud);
|
||||
}
|
||||
|
||||
// Save to dot env
|
||||
|
@ -59,7 +59,7 @@ const install: CommandModule<
|
|||
{
|
||||
p?: string;
|
||||
ss: boolean;
|
||||
oc?: boolean;
|
||||
cloud: boolean;
|
||||
du?: string;
|
||||
}
|
||||
> = {
|
||||
|
@ -78,10 +78,11 @@ const install: CommandModule<
|
|||
type: 'boolean',
|
||||
default: false,
|
||||
},
|
||||
oc: {
|
||||
alias: 'official-connectors',
|
||||
describe: 'Add official connectors after downloading Logto',
|
||||
cloud: {
|
||||
describe: 'Init Logto for cloud',
|
||||
type: 'boolean',
|
||||
hidden: true,
|
||||
default: false,
|
||||
},
|
||||
du: {
|
||||
alias: 'download-url',
|
||||
|
@ -90,8 +91,8 @@ const install: CommandModule<
|
|||
hidden: true,
|
||||
},
|
||||
}),
|
||||
handler: async ({ p, ss, oc, du }) => {
|
||||
await installLogto({ path: p, skipSeed: ss, officialConnectors: oc, downloadUrl: du });
|
||||
handler: async ({ p, ss, cloud, du }) => {
|
||||
await installLogto({ path: p, skipSeed: ss, cloud, downloadUrl: du });
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -133,7 +133,7 @@ export const decompress = async (toPath: string, tarPath: string) => {
|
|||
);
|
||||
};
|
||||
|
||||
export const seedDatabase = async (instancePath: string) => {
|
||||
export const seedDatabase = async (instancePath: string, cloud: boolean) => {
|
||||
try {
|
||||
const pool = await createPoolAndDatabaseIfNeeded();
|
||||
await seedByPool(pool);
|
||||
|
|
|
@ -13,8 +13,9 @@
|
|||
"build": "rm -rf lib/ && tsc -p tsconfig.test.json --sourcemap",
|
||||
"test:only": "NODE_OPTIONS=--experimental-vm-modules jest",
|
||||
"test": "pnpm build && pnpm test:api && pnpm test:ui",
|
||||
"test:api": "pnpm test:only -i ./lib/tests/api",
|
||||
"test:ui": "pnpm test:only -i --config=jest.config.ui.js ./lib/tests/ui",
|
||||
"test:api": "pnpm test:only -i ./lib/tests/api/",
|
||||
"test:ui": "pnpm test:only -i --config=jest.config.ui.js ./lib/tests/ui/",
|
||||
"test:ui-cloud": "pnpm test:only -i --config=jest.config.ui.js ./lib/tests/ui-cloud/",
|
||||
"lint": "eslint --ext .ts src",
|
||||
"lint:report": "pnpm lint --format json --output-file report.json",
|
||||
"start": "pnpm test"
|
||||
|
|
|
@ -6,6 +6,7 @@ export const logtoConsoleUrl = getEnv(
|
|||
'INTEGRATION_TESTS_LOGTO_CONSOLE_URL',
|
||||
'http://localhost:3002'
|
||||
);
|
||||
export const logtoCloudUrl = getEnv('INTEGRATION_TESTS_LOGTO_CLOUD_URL', 'http://localhost:3003');
|
||||
|
||||
export const discoveryUrl = `${logtoUrl}/oidc/.well-known/openid-configuration`;
|
||||
|
||||
|
|
73
packages/integration-tests/src/tests/ui-cloud/smoke.test.ts
Normal file
73
packages/integration-tests/src/tests/ui-cloud/smoke.test.ts
Normal file
|
@ -0,0 +1,73 @@
|
|||
import path from 'path';
|
||||
|
||||
import { logtoCloudUrl as logtoCloudUrlString, logtoConsoleUrl } from '#src/constants.js';
|
||||
import { generatePassword } from '#src/utils.js';
|
||||
|
||||
const appendPathname = (pathname: string, baseUrl: URL) =>
|
||||
new URL(path.join(baseUrl.pathname, pathname), baseUrl);
|
||||
|
||||
/**
|
||||
* NOTE: This test suite assumes test cases will run sequentially (which is Jest default).
|
||||
* Parallel execution will lead to errors.
|
||||
*/
|
||||
describe('smoke testing for cloud', () => {
|
||||
const consoleUsername = 'admin';
|
||||
const consolePassword = generatePassword();
|
||||
const logtoCloudUrl = new URL(logtoCloudUrlString);
|
||||
const adminTenantUrl = new URL(logtoConsoleUrl); // In dev mode, the console URL is actually for admin tenant
|
||||
|
||||
it('opens with app element and navigates to sign-in page', async () => {
|
||||
const navigation = page.waitForNavigation({ waitUntil: 'networkidle0' });
|
||||
await page.goto(logtoCloudUrl.href);
|
||||
await navigation;
|
||||
|
||||
await expect(page.waitForSelector('#app')).resolves.not.toBeNull();
|
||||
expect(page.url()).toBe(appendPathname('/register', adminTenantUrl).href);
|
||||
});
|
||||
|
||||
it('registers the first admin account', async () => {
|
||||
const createAccountButton = await page.waitForSelector('button');
|
||||
expect(createAccountButton).not.toBeNull();
|
||||
|
||||
const usernameField = await page.waitForSelector('input[name=identifier]');
|
||||
const submitButton = await page.waitForSelector('button[name=submit]');
|
||||
|
||||
await usernameField.type(consoleUsername);
|
||||
|
||||
const navigateToCreatePassword = page.waitForNavigation({ waitUntil: 'networkidle0' });
|
||||
await submitButton.click();
|
||||
await navigateToCreatePassword;
|
||||
|
||||
expect(page.url()).toBe(appendPathname('/register/password', adminTenantUrl).href);
|
||||
|
||||
const passwordField = await page.waitForSelector('input[name=newPassword]');
|
||||
const confirmPasswordField = await page.waitForSelector('input[name=confirmPassword]');
|
||||
const saveButton = await page.waitForSelector('button[name=submit]');
|
||||
await passwordField.type(consolePassword);
|
||||
await confirmPasswordField.type(consolePassword);
|
||||
|
||||
const navigateToCloud = page.waitForNavigation({ waitUntil: 'networkidle0' });
|
||||
await saveButton.click();
|
||||
await navigateToCloud;
|
||||
|
||||
expect(page.url()).toBe(logtoCloudUrl.href);
|
||||
});
|
||||
|
||||
it('shows a tenant-select page with two tenants', async () => {
|
||||
const tenantsWrapper = await page.waitForSelector('div[class$=wrapper]');
|
||||
const tenants = await tenantsWrapper.$$('a');
|
||||
const hrefs = await Promise.all(
|
||||
tenants.map(async (element) => {
|
||||
const value = await element.getProperty('href');
|
||||
|
||||
return value.jsonValue();
|
||||
})
|
||||
);
|
||||
|
||||
expect(
|
||||
['default', 'admin'].every((tenantId) =>
|
||||
hrefs.some((href) => String(href).endsWith('/' + tenantId))
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
|
@ -1,6 +1,10 @@
|
|||
import { logtoConsoleUrl } from '#src/constants.js';
|
||||
import { generatePassword } from '#src/utils.js';
|
||||
|
||||
/**
|
||||
* NOTE: This test suite assumes test cases will run sequentially (which is Jest default).
|
||||
* Parallel execution will lead to errors.
|
||||
*/
|
||||
describe('smoke testing', () => {
|
||||
const consoleUsername = 'admin';
|
||||
const consolePassword = generatePassword();
|
||||
|
|
Loading…
Add table
Reference in a new issue