Skip to content

Commit

Permalink
refactor: fix test cases
Browse files Browse the repository at this point in the history
  • Loading branch information
gao-sun committed Mar 29, 2024
1 parent 1246ad6 commit ee39885
Show file tree
Hide file tree
Showing 64 changed files with 335 additions and 323 deletions.
4 changes: 4 additions & 0 deletions packages/integration-tests/jest.setup.js
Expand Up @@ -9,6 +9,10 @@ dotenv.config();
/* eslint-disable @silverhand/fp/no-mutation */
global.TextDecoder = TextDecoder;
global.TextEncoder = TextEncoder;
global.fail = (message) => {
throw new Error(message);
};

/* eslint-enable @silverhand/fp/no-mutation */

// GitHub Actions default runners need more time for UI tests
Expand Down
7 changes: 4 additions & 3 deletions packages/integration-tests/src/api/application.ts
Expand Up @@ -8,8 +8,6 @@ import {
} from '@logto/schemas';
import { conditional } from '@silverhand/essentials';

import { createFormData } from '#src/utils.js';

import { authedAdminApi, oidcApi } from './api.js';

export const createApplication = async (
Expand Down Expand Up @@ -101,7 +99,10 @@ export const generateM2mLog = async (applicationId: string) => {

// This is a token request with insufficient parameters and should fail. We make the request to generate a log for the current machine to machine app.
return oidcApi.post('token', {
body: createFormData({
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
client_id: id,
client_secret: secret,
grant_type: 'client_credentials',
Expand Down
30 changes: 26 additions & 4 deletions packages/integration-tests/src/api/factory.ts
@@ -1,5 +1,23 @@
import { authedAdminApi } from './api.js';

/**
* Transform the data to a new object or array without modifying the original data.
* This is useful since `.json()` returns an object that contains something which makes
* it impossible to use `.toStrictEqual()` directly.
*/
const transform = <T>(data: T): T => {
if (Array.isArray(data)) {
// eslint-disable-next-line no-restricted-syntax
return [...data] as T;
}

if (typeof data === 'object') {
return { ...data };
}

return data;
};

export class ApiFactory<
Schema extends Record<string, unknown>,
PostData extends Record<string, unknown>,
Expand All @@ -8,19 +26,23 @@ export class ApiFactory<
constructor(public readonly path: string) {}

async create(data: PostData): Promise<Schema> {
return authedAdminApi.post(this.path, { json: data }).json<Schema>();
return transform(await authedAdminApi.post(this.path, { json: data }).json<Schema>());
}

async getList(params?: URLSearchParams): Promise<Schema[]> {
return authedAdminApi.get(this.path + '?' + (params?.toString() ?? '')).json<Schema[]>();
return transform(
await authedAdminApi.get(this.path + '?' + (params?.toString() ?? '')).json<Schema[]>()
);
}

async get(id: string): Promise<Schema> {
return authedAdminApi.get(this.path + '/' + id).json<Schema>();
return transform(await authedAdminApi.get(this.path + '/' + id).json<Schema>());
}

async update(id: string, data: PatchData): Promise<Schema> {
return authedAdminApi.patch(this.path + '/' + id, { json: data }).json<Schema>();
return transform(
await authedAdminApi.patch(this.path + '/' + id, { json: data }).json<Schema>()
);
}

async delete(id: string): Promise<void> {
Expand Down
1 change: 0 additions & 1 deletion packages/integration-tests/src/api/interaction-sso.ts
Expand Up @@ -15,7 +15,6 @@ export const getSsoAuthorizationUrl = async (
.post(`interaction/${ssoPath}/${connectorId}/authorization-url`, {
headers: { cookie },
json: payload,
redirect: 'manual',
})
.json<{ redirectTo: string }>();
};
Expand Down
15 changes: 2 additions & 13 deletions packages/integration-tests/src/api/interaction.ts
Expand Up @@ -26,15 +26,13 @@ export const putInteraction = async (cookie: string, payload: InteractionPayload
.put('interaction', {
headers: { cookie },
json: payload,
redirect: 'manual',
})
.json();

export const deleteInteraction = async (cookie: string) =>
api
.delete('interaction', {
headers: { cookie },
redirect: 'manual',
})
.json();

Expand All @@ -46,7 +44,6 @@ export const patchInteractionIdentifiers = async (cookie: string, payload: Ident
.patch('interaction/identifiers', {
headers: { cookie },
json: payload,
redirect: 'manual',
})
.json();

Expand All @@ -55,7 +52,6 @@ export const patchInteractionProfile = async (cookie: string, payload: Profile)
.patch('interaction/profile', {
headers: { cookie },
json: payload,
redirect: 'manual',
})
.json();

Expand All @@ -64,7 +60,6 @@ export const putInteractionProfile = async (cookie: string, payload: Profile) =>
.put('interaction/profile', {
headers: { cookie },
json: payload,
redirect: 'manual',
})
.json();

Expand All @@ -73,7 +68,6 @@ export const postInteractionBindMfa = async (cookie: string, payload: BindMfaPay
.post('interaction/bind-mfa', {
headers: { cookie },
json: payload,
redirect: 'manual',
})
.json();

Expand All @@ -82,15 +76,13 @@ export const putInteractionMfa = async (cookie: string, payload: VerifyMfaPayloa
.put('interaction/mfa', {
headers: { cookie },
json: payload,
redirect: 'manual',
})
.json();

export const deleteInteractionProfile = async (cookie: string) =>
api
.delete('interaction/profile', {
headers: { cookie },
redirect: 'manual',
})
.json();

Expand All @@ -106,7 +98,6 @@ export const sendVerificationCode = async (
api.post('interaction/verification/verification-code', {
headers: { cookie },
json: payload,
redirect: 'manual',
});

export type SocialAuthorizationUriPayload = {
Expand All @@ -122,7 +113,6 @@ export const createSocialAuthorizationUri = async (
api.post('interaction/verification/social-authorization-uri', {
headers: { cookie },
json: payload,
redirect: 'manual',
});

export const initTotp = async (cookie: string) =>
Expand All @@ -131,6 +121,7 @@ export const initTotp = async (cookie: string) =>
headers: { cookie },
json: {},
redirect: 'manual',
throwHttpErrors: false,
})
.json<{ secret: string }>();

Expand All @@ -140,7 +131,6 @@ export const skipMfaBinding = async (cookie: string) =>
json: {
mfaSkipped: true,
},
redirect: 'manual',
});

export const consent = async (api: KyInstance, cookie: string) =>
Expand All @@ -150,14 +140,14 @@ export const consent = async (api: KyInstance, cookie: string) =>
cookie,
},
redirect: 'manual',
throwHttpErrors: false,
})
.json<RedirectResponse>();

export const getConsentInfo = async (cookie: string) =>
api
.get('interaction/consent', {
headers: { cookie },
redirect: 'manual',
})
.json<ConsentInfoResponse>();

Expand All @@ -168,5 +158,4 @@ export const createSingleSignOnAuthorizationUri = async (
api.post('interaction/verification/sso-authorization-uri', {
headers: { cookie },
json: payload,
redirect: 'manual',
});
4 changes: 4 additions & 0 deletions packages/integration-tests/src/client/index.ts
Expand Up @@ -73,6 +73,7 @@ export default class MockClient {
// Mock SDK sign-in navigation
const response = await ky(this.navigateUrl, {
redirect: 'manual',
throwHttpErrors: false,
});

// Note: should redirect to sign-in page
Expand Down Expand Up @@ -106,6 +107,7 @@ export default class MockClient {
cookie: this.interactionCookie,
},
redirect: 'manual',
throwHttpErrors: false,
});

// Note: Should redirect to logto consent page
Expand Down Expand Up @@ -182,6 +184,7 @@ export default class MockClient {
cookie: this.interactionCookie,
},
redirect: 'manual',
throwHttpErrors: false,
});

// Consent page should auto consent and redirect to auth endpoint
Expand All @@ -196,6 +199,7 @@ export default class MockClient {
cookie: this.interactionCookie,
},
redirect: 'manual',
throwHttpErrors: false,
});

// Note: Should redirect to the signInCallbackUri
Expand Down
1 change: 1 addition & 0 deletions packages/integration-tests/src/helpers/admin-tenant.ts
Expand Up @@ -63,6 +63,7 @@ export const putInteraction = async (cookie: string, payload: InteractionPayload
headers: { cookie },
json: payload,
redirect: 'manual',
throwHttpErrors: false,
})
.json();

Expand Down
10 changes: 5 additions & 5 deletions packages/integration-tests/src/helpers/index.ts
Expand Up @@ -77,7 +77,7 @@ export const removeConnectorMessage = async (

type ExpectedErrorInfo = {
code: string;
statusCode: number;
status: number;
messageIncludes?: string;
};

Expand All @@ -94,24 +94,24 @@ export const expectRejects = async <T = void>(
fail();
};

export const expectRequestError = <T = void>(error: unknown, expected: ExpectedErrorInfo) => {
const { code, statusCode, messageIncludes } = expected;
const expectRequestError = async <T = void>(error: unknown, expected: ExpectedErrorInfo) => {
const { code, status, messageIncludes } = expected;

if (!(error instanceof HTTPError)) {
fail('Error should be an instance of RequestError');
}

// JSON.parse returns `any`. Directly use `as` since we've already know the response body structure.
// eslint-disable-next-line no-restricted-syntax
const body = JSON.parse(String(error.response.body)) as {
const body = (await error.response.json()) as {
code: string;
message: string;
data: T;
};

expect(body.code).toEqual(code);

expect(error.response.status).toEqual(statusCode);
expect(error.response.status).toEqual(status);

if (messageIncludes) {
expect(body.message.includes(messageIncludes)).toBeTruthy();
Expand Down
2 changes: 1 addition & 1 deletion packages/integration-tests/src/helpers/interactions.ts
Expand Up @@ -80,7 +80,7 @@ export const createNewSocialUserWithUsernameAndPassword = async (connectorId: st

await expectRejects(client.submitInteraction(), {
code: 'user.identity_not_exist',
statusCode: 422,
status: 422,
});
await client.successSend(patchInteractionIdentifiers, { username, password });
await client.successSend(putInteractionProfile, { connectorId });
Expand Down
Expand Up @@ -21,7 +21,7 @@ describe('admin console user management (roles)', () => {
const m2mRole = await createRole({ type: RoleType.MachineToMachine });
await expectRejects(assignRolesToUser(user.id, [m2mRole.id]), {
code: 'user.invalid_role_type',
statusCode: 422,
status: 422,
});

await assignRolesToUser(user.id, [role1.id, role2.id]);
Expand Down
Expand Up @@ -9,11 +9,11 @@ import { UserApiTest } from '#src/helpers/user.js';
const getUsers = async <T>(
init: string[][] | Record<string, string> | URLSearchParams
): Promise<{ headers: Headers; json: T }> => {
const { headers, json } = await authedAdminApi.get('users', {
const response = await authedAdminApi.get('users', {
searchParams: new URLSearchParams(init),
});

return { headers, json: (await json()) as T };
return { headers: response.headers, json: (await response.json()) as T };
};

describe('admin console user search params', () => {
Expand Down Expand Up @@ -172,7 +172,7 @@ describe('admin console user search params', () => {
['search.primaryEmail', 'jerry_swift_jr_2@geek.best'],
['search.primaryEmail', 'jerry_swift_jr_jr@gmail.com'],
]),
{ code: 'request.invalid_input', statusCode: 400, messageIncludes: '`exact`' }
{ code: 'request.invalid_input', status: 400, messageIncludes: '`exact`' }
);
});

Expand All @@ -184,7 +184,7 @@ describe('admin console user search params', () => {
]),
{
code: 'request.invalid_input',
statusCode: 400,
status: 400,
messageIncludes: 'cannot be empty',
}
);
Expand All @@ -198,7 +198,7 @@ describe('admin console user search params', () => {
]),
{
code: 'request.invalid_input',
statusCode: 400,
status: 400,
messageIncludes: 'case-insensitive',
}
);
Expand All @@ -213,13 +213,13 @@ describe('admin console user search params', () => {
]),
{
code: 'request.invalid_input',
statusCode: 400,
status: 400,
messageIncludes: 'is not valid',
}
),
expectRejects(getUsers<User[]>([['search.email', '%gmail%']]), {
code: 'request.invalid_input',
statusCode: 400,
status: 400,
messageIncludes: 'is not valid',
}),
expectRejects(
Expand All @@ -229,7 +229,7 @@ describe('admin console user search params', () => {
]),
{
code: 'request.invalid_input',
statusCode: 400,
status: 400,
messageIncludes: 'is not valid',
}
),
Expand Down

0 comments on commit ee39885

Please sign in to comment.