Refactor kcContext, avoid having mocks in the dist https://github.com/keycloakify/keycloakify/discussions/299#discussioncomment-9616747
This commit is contained in:
@ -7,7 +7,6 @@ import {
|
||||
import { replaceImportsInInlineCssCode } from "keycloakify/bin/keycloakify/replacers/replaceImportsInInlineCssCode";
|
||||
import { same } from "evt/tools/inDepth/same";
|
||||
import { expect, it, describe } from "vitest";
|
||||
import { isSameCode } from "../tools/isSameCode";
|
||||
import {
|
||||
basenameOfTheKeycloakifyResourcesDir,
|
||||
nameOfTheGlobal
|
||||
@ -664,3 +663,10 @@ describe("inline css replacer", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
export function isSameCode(code1: string, code2: string): boolean {
|
||||
const removeSpacesAndNewLines = (code: string) =>
|
||||
code.replace(/\s/g, "").replace(/\n/g, "");
|
||||
|
||||
return removeSpacesAndNewLines(code1) === removeSpacesAndNewLines(code2);
|
||||
}
|
||||
|
@ -1,303 +0,0 @@
|
||||
import { createGetKcContext } from "keycloakify/login/kcContext/createGetKcContext";
|
||||
import type { ExtendKcContext } from "keycloakify/login/kcContext/getKcContextFromWindow";
|
||||
import type { KcContext } from "keycloakify/login/kcContext";
|
||||
import { same } from "evt/tools/inDepth";
|
||||
import { assert } from "tsafe/assert";
|
||||
import type { Equals } from "tsafe";
|
||||
import {
|
||||
kcContextMocks,
|
||||
kcContextCommonMock
|
||||
} from "keycloakify/login/kcContext/kcContextMocks";
|
||||
import { deepClone } from "keycloakify/tools/deepClone";
|
||||
import { expect, it, describe } from "vitest";
|
||||
|
||||
describe("createGetKcContext", () => {
|
||||
const authorizedMailDomains = [
|
||||
"example.com",
|
||||
"another-example.com",
|
||||
"*.yet-another-example.com",
|
||||
"*.example.com",
|
||||
"hello-world.com"
|
||||
];
|
||||
|
||||
const displayName = "this is an overwritten common value";
|
||||
|
||||
const aNonStandardValue1 = "a non standard value 1";
|
||||
const aNonStandardValue2 = "a non standard value 2";
|
||||
|
||||
type KcContextExtension =
|
||||
| {
|
||||
pageId: "register.ftl";
|
||||
authorizedMailDomains: string[];
|
||||
}
|
||||
| {
|
||||
pageId: "info.ftl";
|
||||
aNonStandardValue1: string;
|
||||
}
|
||||
| {
|
||||
pageId: "my-extra-page-1.ftl";
|
||||
}
|
||||
| {
|
||||
pageId: "my-extra-page-2.ftl";
|
||||
aNonStandardValue2: string;
|
||||
};
|
||||
|
||||
const getKcContextProxy = (params: {
|
||||
mockPageId: ExtendKcContext<KcContextExtension>["pageId"];
|
||||
}) => {
|
||||
const { mockPageId } = params;
|
||||
|
||||
const { getKcContext } = createGetKcContext<KcContextExtension>({
|
||||
mockData: [
|
||||
{
|
||||
pageId: "login.ftl",
|
||||
realm: { displayName }
|
||||
},
|
||||
{
|
||||
pageId: "info.ftl",
|
||||
aNonStandardValue1
|
||||
},
|
||||
{
|
||||
pageId: "register.ftl",
|
||||
authorizedMailDomains
|
||||
},
|
||||
{
|
||||
pageId: "my-extra-page-2.ftl",
|
||||
aNonStandardValue2
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const { kcContext } = getKcContext({
|
||||
mockPageId
|
||||
});
|
||||
|
||||
return { kcContext };
|
||||
};
|
||||
it("has proper API for login.ftl", () => {
|
||||
const pageId = "login.ftl";
|
||||
|
||||
const { kcContext } = getKcContextProxy({ mockPageId: pageId });
|
||||
|
||||
assert(kcContext?.pageId === pageId);
|
||||
|
||||
assert<Equals<typeof kcContext, KcContext.Login>>();
|
||||
|
||||
expect(
|
||||
same(
|
||||
//NOTE: deepClone for printIfExists or other functions...
|
||||
deepClone(kcContext),
|
||||
(() => {
|
||||
const mock = deepClone(
|
||||
kcContextMocks.find(
|
||||
({ pageId: pageId_i }) => pageId_i === pageId
|
||||
)!
|
||||
);
|
||||
|
||||
mock.realm.displayName = displayName;
|
||||
|
||||
return mock;
|
||||
})()
|
||||
)
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("has a proper API for info.ftl", () => {
|
||||
const pageId = "info.ftl";
|
||||
|
||||
const { kcContext } = getKcContextProxy({ mockPageId: pageId });
|
||||
|
||||
assert(kcContext?.pageId === pageId);
|
||||
|
||||
//NOTE: I don't understand the need to add: pageId: typeof pageId; ...
|
||||
assert<
|
||||
Equals<
|
||||
typeof kcContext,
|
||||
KcContext.Info & {
|
||||
pageId: typeof pageId;
|
||||
aNonStandardValue1: string;
|
||||
}
|
||||
>
|
||||
>();
|
||||
|
||||
expect(
|
||||
same(
|
||||
deepClone(kcContext),
|
||||
(() => {
|
||||
const mock = deepClone(
|
||||
kcContextMocks.find(
|
||||
({ pageId: pageId_i }) => pageId_i === pageId
|
||||
)!
|
||||
);
|
||||
|
||||
Object.assign(mock, { aNonStandardValue1 });
|
||||
|
||||
return mock;
|
||||
})()
|
||||
)
|
||||
).toBe(true);
|
||||
});
|
||||
it("has a proper API for register.ftl", () => {
|
||||
const pageId = "register.ftl";
|
||||
|
||||
const { kcContext } = getKcContextProxy({ mockPageId: pageId });
|
||||
|
||||
assert(kcContext?.pageId === pageId);
|
||||
|
||||
//NOTE: I don't understand the need to add: pageId: typeof pageId; ...
|
||||
assert<
|
||||
Equals<
|
||||
typeof kcContext,
|
||||
KcContext.Register & {
|
||||
pageId: typeof pageId;
|
||||
authorizedMailDomains: string[];
|
||||
}
|
||||
>
|
||||
>();
|
||||
|
||||
expect(
|
||||
same(
|
||||
deepClone(kcContext),
|
||||
(() => {
|
||||
const mock = deepClone(
|
||||
kcContextMocks.find(
|
||||
({ pageId: pageId_i }) => pageId_i === pageId
|
||||
)!
|
||||
);
|
||||
|
||||
Object.assign(mock, { authorizedMailDomains });
|
||||
|
||||
return mock;
|
||||
})()
|
||||
)
|
||||
).toBe(true);
|
||||
});
|
||||
it("has a proper API for my-extra-page-2.ftl", () => {
|
||||
const pageId = "my-extra-page-2.ftl";
|
||||
|
||||
const { kcContext } = getKcContextProxy({ mockPageId: pageId });
|
||||
|
||||
assert(kcContext?.pageId === pageId);
|
||||
|
||||
assert<
|
||||
Equals<
|
||||
typeof kcContext,
|
||||
KcContext.Common & {
|
||||
pageId: typeof pageId;
|
||||
aNonStandardValue2: string;
|
||||
}
|
||||
>
|
||||
>();
|
||||
|
||||
kcContext.aNonStandardValue2;
|
||||
|
||||
expect(
|
||||
same(
|
||||
deepClone(kcContext),
|
||||
(() => {
|
||||
const mock = deepClone(kcContextCommonMock);
|
||||
|
||||
Object.assign(mock, { pageId, aNonStandardValue2 });
|
||||
|
||||
return mock;
|
||||
})()
|
||||
)
|
||||
).toBe(true);
|
||||
});
|
||||
it("has a proper API for my-extra-page-1.ftl", () => {
|
||||
const pageId = "my-extra-page-1.ftl";
|
||||
|
||||
console.log("We expect a warning here =>");
|
||||
|
||||
const { kcContext } = getKcContextProxy({ mockPageId: pageId });
|
||||
|
||||
assert(kcContext?.pageId === pageId);
|
||||
|
||||
assert<Equals<typeof kcContext, KcContext.Common & { pageId: typeof pageId }>>();
|
||||
|
||||
expect(
|
||||
same(
|
||||
deepClone(kcContext),
|
||||
(() => {
|
||||
const mock = deepClone(kcContextCommonMock);
|
||||
|
||||
Object.assign(mock, { pageId });
|
||||
|
||||
return mock;
|
||||
})()
|
||||
)
|
||||
).toBe(true);
|
||||
});
|
||||
it("returns the proper mock for login.ftl", () => {
|
||||
const pageId = "login.ftl";
|
||||
|
||||
const { getKcContext } = createGetKcContext();
|
||||
|
||||
const { kcContext } = getKcContext({
|
||||
mockPageId: pageId
|
||||
});
|
||||
|
||||
assert<Equals<typeof kcContext, KcContext.Login>>();
|
||||
|
||||
assert(
|
||||
same(
|
||||
deepClone(kcContext),
|
||||
deepClone(
|
||||
kcContextMocks.find(({ pageId: pageId_i }) => pageId_i === pageId)!
|
||||
)
|
||||
)
|
||||
);
|
||||
});
|
||||
it("returns undefined when no mock is specified", () => {
|
||||
const { getKcContext } = createGetKcContext();
|
||||
|
||||
const { kcContext } = getKcContext();
|
||||
|
||||
assert<Equals<typeof kcContext, KcContext | undefined>>();
|
||||
|
||||
assert(kcContext === undefined);
|
||||
});
|
||||
|
||||
it("mock are properly overwritten", () => {
|
||||
const { getKcContext } = createGetKcContext();
|
||||
|
||||
const displayName = "myDisplayName";
|
||||
|
||||
const { kcContext } = getKcContext({
|
||||
mockPageId: "login.ftl",
|
||||
storyPartialKcContext: {
|
||||
realm: {
|
||||
displayName
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
assert<Equals<typeof kcContext, KcContext.Login>>();
|
||||
|
||||
assert(kcContext?.realm.displayName === displayName);
|
||||
});
|
||||
|
||||
it("mockPageId doesn't have to be a singleton", () => {
|
||||
const { getKcContext } = createGetKcContext();
|
||||
|
||||
const mockPageId: "login.ftl" | "register.ftl" = "login.ftl" as any;
|
||||
|
||||
const { kcContext } = getKcContext({
|
||||
mockPageId
|
||||
});
|
||||
|
||||
assert<Equals<typeof kcContext, KcContext.Login | KcContext.Register>>();
|
||||
});
|
||||
|
||||
it("no undefined as long as we provide a mock pageId", () => {
|
||||
const { getKcContext } = createGetKcContext();
|
||||
|
||||
const mockPageId: KcContext["pageId"] = "login.ftl" as any;
|
||||
|
||||
const { kcContext } = getKcContext({
|
||||
mockPageId
|
||||
});
|
||||
|
||||
assert<Equals<typeof kcContext, KcContext>>();
|
||||
});
|
||||
});
|
@ -1,253 +0,0 @@
|
||||
import { getKcContext } from "keycloakify/login/kcContext/getKcContext";
|
||||
import type { ExtendKcContext } from "keycloakify/login/kcContext/getKcContextFromWindow";
|
||||
import type { KcContext } from "keycloakify/login/kcContext";
|
||||
import { same } from "evt/tools/inDepth";
|
||||
import { assert } from "tsafe/assert";
|
||||
import type { Equals } from "tsafe";
|
||||
import {
|
||||
kcContextMocks,
|
||||
kcContextCommonMock
|
||||
} from "keycloakify/login/kcContext/kcContextMocks";
|
||||
import { deepClone } from "keycloakify/tools/deepClone";
|
||||
import { expect, it, describe } from "vitest";
|
||||
|
||||
describe("getKcContext", () => {
|
||||
const authorizedMailDomains = [
|
||||
"example.com",
|
||||
"another-example.com",
|
||||
"*.yet-another-example.com",
|
||||
"*.example.com",
|
||||
"hello-world.com"
|
||||
];
|
||||
|
||||
const displayName = "this is an overwritten common value";
|
||||
|
||||
const aNonStandardValue1 = "a non standard value 1";
|
||||
const aNonStandardValue2 = "a non standard value 2";
|
||||
|
||||
type KcContextExtension =
|
||||
| {
|
||||
pageId: "register.ftl";
|
||||
authorizedMailDomains: string[];
|
||||
}
|
||||
| {
|
||||
pageId: "info.ftl";
|
||||
aNonStandardValue1: string;
|
||||
}
|
||||
| {
|
||||
pageId: "my-extra-page-1.ftl";
|
||||
}
|
||||
| {
|
||||
pageId: "my-extra-page-2.ftl";
|
||||
aNonStandardValue2: string;
|
||||
};
|
||||
|
||||
const getKcContextProxy = (params: {
|
||||
mockPageId: ExtendKcContext<KcContextExtension>["pageId"];
|
||||
}) => {
|
||||
const { mockPageId } = params;
|
||||
|
||||
const { kcContext } = getKcContext<KcContextExtension>({
|
||||
mockPageId,
|
||||
mockData: [
|
||||
{
|
||||
pageId: "login.ftl",
|
||||
realm: { displayName }
|
||||
},
|
||||
{
|
||||
pageId: "info.ftl",
|
||||
aNonStandardValue1
|
||||
},
|
||||
{
|
||||
pageId: "register.ftl",
|
||||
authorizedMailDomains
|
||||
},
|
||||
{
|
||||
pageId: "my-extra-page-2.ftl",
|
||||
aNonStandardValue2
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
return { kcContext };
|
||||
};
|
||||
it("has proper API for login.ftl", () => {
|
||||
const pageId = "login.ftl";
|
||||
|
||||
const { kcContext } = getKcContextProxy({ mockPageId: pageId });
|
||||
|
||||
assert(kcContext?.pageId === pageId);
|
||||
|
||||
assert<Equals<typeof kcContext, KcContext.Login>>();
|
||||
|
||||
expect(
|
||||
same(
|
||||
//NOTE: deepClone for printIfExists or other functions...
|
||||
deepClone(kcContext),
|
||||
(() => {
|
||||
const mock = deepClone(
|
||||
kcContextMocks.find(
|
||||
({ pageId: pageId_i }) => pageId_i === pageId
|
||||
)!
|
||||
);
|
||||
|
||||
mock.realm.displayName = displayName;
|
||||
|
||||
return mock;
|
||||
})()
|
||||
)
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("has a proper API for info.ftl", () => {
|
||||
const pageId = "info.ftl";
|
||||
|
||||
const { kcContext } = getKcContextProxy({ mockPageId: pageId });
|
||||
|
||||
assert(kcContext?.pageId === pageId);
|
||||
|
||||
//NOTE: I don't understand the need to add: pageId: typeof pageId; ...
|
||||
assert<
|
||||
Equals<
|
||||
typeof kcContext,
|
||||
KcContext.Info & {
|
||||
pageId: typeof pageId;
|
||||
aNonStandardValue1: string;
|
||||
}
|
||||
>
|
||||
>();
|
||||
|
||||
expect(
|
||||
same(
|
||||
deepClone(kcContext),
|
||||
(() => {
|
||||
const mock = deepClone(
|
||||
kcContextMocks.find(
|
||||
({ pageId: pageId_i }) => pageId_i === pageId
|
||||
)!
|
||||
);
|
||||
|
||||
Object.assign(mock, { aNonStandardValue1 });
|
||||
|
||||
return mock;
|
||||
})()
|
||||
)
|
||||
).toBe(true);
|
||||
});
|
||||
it("has a proper API for register.ftl", () => {
|
||||
const pageId = "register.ftl";
|
||||
|
||||
const { kcContext } = getKcContextProxy({ mockPageId: pageId });
|
||||
|
||||
assert(kcContext?.pageId === pageId);
|
||||
|
||||
//NOTE: I don't understand the need to add: pageId: typeof pageId; ...
|
||||
assert<
|
||||
Equals<
|
||||
typeof kcContext,
|
||||
KcContext.Register & {
|
||||
pageId: typeof pageId;
|
||||
authorizedMailDomains: string[];
|
||||
}
|
||||
>
|
||||
>();
|
||||
|
||||
expect(
|
||||
same(
|
||||
deepClone(kcContext),
|
||||
(() => {
|
||||
const mock = deepClone(
|
||||
kcContextMocks.find(
|
||||
({ pageId: pageId_i }) => pageId_i === pageId
|
||||
)!
|
||||
);
|
||||
|
||||
Object.assign(mock, { authorizedMailDomains });
|
||||
|
||||
return mock;
|
||||
})()
|
||||
)
|
||||
).toBe(true);
|
||||
});
|
||||
it("has a proper API for my-extra-page-2.ftl", () => {
|
||||
const pageId = "my-extra-page-2.ftl";
|
||||
|
||||
const { kcContext } = getKcContextProxy({ mockPageId: pageId });
|
||||
|
||||
assert(kcContext?.pageId === pageId);
|
||||
|
||||
assert<
|
||||
Equals<
|
||||
typeof kcContext,
|
||||
KcContext.Common & {
|
||||
pageId: typeof pageId;
|
||||
aNonStandardValue2: string;
|
||||
}
|
||||
>
|
||||
>();
|
||||
|
||||
kcContext.aNonStandardValue2;
|
||||
|
||||
expect(
|
||||
same(
|
||||
deepClone(kcContext),
|
||||
(() => {
|
||||
const mock = deepClone(kcContextCommonMock);
|
||||
|
||||
Object.assign(mock, { pageId, aNonStandardValue2 });
|
||||
|
||||
return mock;
|
||||
})()
|
||||
)
|
||||
).toBe(true);
|
||||
});
|
||||
it("has a proper API for my-extra-page-1.ftl", () => {
|
||||
const pageId = "my-extra-page-1.ftl";
|
||||
|
||||
console.log("We expect a warning here =>");
|
||||
|
||||
const { kcContext } = getKcContextProxy({ mockPageId: pageId });
|
||||
|
||||
assert(kcContext?.pageId === pageId);
|
||||
|
||||
assert<Equals<typeof kcContext, KcContext.Common & { pageId: typeof pageId }>>();
|
||||
|
||||
expect(
|
||||
same(
|
||||
deepClone(kcContext),
|
||||
(() => {
|
||||
const mock = deepClone(kcContextCommonMock);
|
||||
|
||||
Object.assign(mock, { pageId });
|
||||
|
||||
return mock;
|
||||
})()
|
||||
)
|
||||
).toBe(true);
|
||||
});
|
||||
it("returns the proper mock for login.ftl", () => {
|
||||
const pageId = "login.ftl";
|
||||
|
||||
const { kcContext } = getKcContext({
|
||||
mockPageId: pageId
|
||||
});
|
||||
|
||||
assert<Equals<typeof kcContext, KcContext | undefined>>();
|
||||
|
||||
assert(
|
||||
same(
|
||||
deepClone(kcContext),
|
||||
deepClone(
|
||||
kcContextMocks.find(({ pageId: pageId_i }) => pageId_i === pageId)!
|
||||
)
|
||||
)
|
||||
);
|
||||
});
|
||||
it("returns undefined when no mock is specified", () => {
|
||||
const { kcContext } = getKcContext();
|
||||
|
||||
assert<Equals<typeof kcContext, KcContext | undefined>>();
|
||||
|
||||
assert(kcContext === undefined);
|
||||
});
|
||||
});
|
@ -1,100 +0,0 @@
|
||||
import { AndByDiscriminatingKey } from "keycloakify/tools/AndByDiscriminatingKey";
|
||||
import { assert } from "tsafe/assert";
|
||||
import type { Equals } from "tsafe";
|
||||
|
||||
{
|
||||
type Base =
|
||||
| { pageId: "a"; onlyA: string }
|
||||
| { pageId: "b"; onlyB: string }
|
||||
| { pageId: "only base"; onlyBase: string };
|
||||
|
||||
type Extension =
|
||||
| { pageId: "a"; onlyExtA: string }
|
||||
| { pageId: "b"; onlyExtB: string }
|
||||
| { pageId: "only ext"; onlyExt: string };
|
||||
|
||||
type Got = AndByDiscriminatingKey<"pageId", Extension, Base>;
|
||||
|
||||
type Expected =
|
||||
| { pageId: "a"; onlyA: string; onlyExtA: string }
|
||||
| { pageId: "b"; onlyB: string; onlyExtB: string }
|
||||
| { pageId: "only base"; onlyBase: string }
|
||||
| { pageId: "only ext"; onlyExt: string };
|
||||
|
||||
assert<Equals<Got, Expected>>();
|
||||
|
||||
const x: Got = null as any;
|
||||
|
||||
if (x.pageId === "a") {
|
||||
x.onlyA;
|
||||
x.onlyExtA;
|
||||
|
||||
//@ts-expect-error
|
||||
x.onlyB;
|
||||
|
||||
//@ts-expect-error
|
||||
x.onlyBase;
|
||||
|
||||
//@ts-expect-error
|
||||
x.onlyExt;
|
||||
}
|
||||
|
||||
if (x.pageId === "b") {
|
||||
x.onlyB;
|
||||
x.onlyExtB;
|
||||
|
||||
//@ts-expect-error
|
||||
x.onlyA;
|
||||
|
||||
//@ts-expect-error
|
||||
x.onlyBase;
|
||||
|
||||
//@ts-expect-error
|
||||
x.onlyExt;
|
||||
}
|
||||
|
||||
if (x.pageId === "only base") {
|
||||
x.onlyBase;
|
||||
|
||||
//@ts-expect-error
|
||||
x.onlyA;
|
||||
|
||||
//@ts-expect-error
|
||||
x.onlyB;
|
||||
|
||||
//@ts-expect-error
|
||||
x.onlyExt;
|
||||
}
|
||||
|
||||
if (x.pageId === "only ext") {
|
||||
x.onlyExt;
|
||||
|
||||
//@ts-expect-error
|
||||
x.onlyA;
|
||||
|
||||
//@ts-expect-error
|
||||
x.onlyB;
|
||||
|
||||
//@ts-expect-error
|
||||
x.onlyBase;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
type Base =
|
||||
| { pageId: "a"; onlyA: string }
|
||||
| { pageId: "b"; onlyB: string }
|
||||
| { pageId: "only base"; onlyBase: string };
|
||||
|
||||
type Extension = { pageId: "only ext"; onlyExt: string };
|
||||
|
||||
type Got = AndByDiscriminatingKey<"pageId", Extension, Base>;
|
||||
|
||||
type Expected =
|
||||
| { pageId: "a"; onlyA: string }
|
||||
| { pageId: "b"; onlyB: string }
|
||||
| { pageId: "only base"; onlyBase: string }
|
||||
| { pageId: "only ext"; onlyExt: string };
|
||||
|
||||
assert<Equals<Got, Expected>>();
|
||||
}
|
211
test/login/kcContext.typelevel-spec.ts
Normal file
211
test/login/kcContext.typelevel-spec.ts
Normal file
@ -0,0 +1,211 @@
|
||||
import { type ExtendKcContext, createGetKcContextMock } from "keycloakify/login";
|
||||
import { KcContext as KcContextBase } from "keycloakify/login/kcContext/KcContext";
|
||||
import { assert, type Equals } from "tsafe/assert";
|
||||
import { Reflect } from "tsafe/Reflect";
|
||||
|
||||
{
|
||||
type KcContextExtraProperties = {
|
||||
properties: {
|
||||
myCustomProperty: string | undefined;
|
||||
};
|
||||
};
|
||||
|
||||
type KcContextExtraPropertiesPerPage = {
|
||||
"login.ftl": {
|
||||
foo: string;
|
||||
};
|
||||
"my-custom-page.ftl": {
|
||||
bar: number;
|
||||
};
|
||||
};
|
||||
|
||||
type KcContext = ExtendKcContext<
|
||||
KcContextExtraProperties,
|
||||
KcContextExtraPropertiesPerPage
|
||||
>;
|
||||
|
||||
{
|
||||
type Got = Extract<KcContext, { pageId: "login.ftl" }>;
|
||||
type Expected = KcContextBase.Login & {
|
||||
properties: { myCustomProperty: string | undefined };
|
||||
} & { foo: string };
|
||||
|
||||
assert<Equals<Got, Expected>>();
|
||||
}
|
||||
|
||||
{
|
||||
type Got = Extract<KcContext, { pageId: "register.ftl" }>;
|
||||
type Expected = KcContextBase.Register & {
|
||||
properties: { myCustomProperty: string | undefined };
|
||||
};
|
||||
|
||||
assert<Equals<Got, Expected>>();
|
||||
}
|
||||
|
||||
{
|
||||
type Got = Extract<KcContext, { pageId: "my-custom-page.ftl" }>;
|
||||
|
||||
type Expected = KcContextBase.Common &
|
||||
KcContextExtraProperties & { pageId: "my-custom-page.ftl" } & {
|
||||
properties: { myCustomProperty: string | undefined };
|
||||
} & { bar: number };
|
||||
|
||||
assert<Got extends Expected ? true : false>();
|
||||
assert<Expected extends Got ? true : false>();
|
||||
}
|
||||
|
||||
const { getKcContextMock } = createGetKcContextMock({
|
||||
kcContextExtraProperties: Reflect<KcContextExtraProperties>(),
|
||||
kcContextExtraPropertiesPerPage: Reflect<KcContextExtraPropertiesPerPage>()
|
||||
});
|
||||
|
||||
{
|
||||
const got = getKcContextMock({
|
||||
pageId: "login.ftl"
|
||||
});
|
||||
|
||||
type Expected = Extract<KcContext, { pageId: "login.ftl" }>;
|
||||
|
||||
assert<Equals<typeof got, Expected>>();
|
||||
}
|
||||
|
||||
{
|
||||
const got = getKcContextMock({
|
||||
pageId: "register.ftl"
|
||||
});
|
||||
|
||||
type Expected = Extract<KcContext, { pageId: "register.ftl" }>;
|
||||
|
||||
assert<Equals<typeof got, Expected>>();
|
||||
}
|
||||
|
||||
{
|
||||
const got = getKcContextMock({
|
||||
pageId: "my-custom-page.ftl"
|
||||
});
|
||||
|
||||
type Expected = Extract<KcContext, { pageId: "my-custom-page.ftl" }>;
|
||||
|
||||
assert<Equals<typeof got, Expected>>();
|
||||
}
|
||||
|
||||
getKcContextMock({
|
||||
// @ts-expect-error
|
||||
pageId: "non-existing-page.ftl"
|
||||
});
|
||||
|
||||
getKcContextMock({
|
||||
pageId: "login.ftl",
|
||||
overrides: {
|
||||
// @ts-expect-error
|
||||
bar: 42
|
||||
}
|
||||
});
|
||||
|
||||
createGetKcContextMock({
|
||||
kcContextExtraProperties: Reflect<KcContextExtraProperties>(),
|
||||
kcContextExtraPropertiesPerPage: Reflect<KcContextExtraPropertiesPerPage>(),
|
||||
overrides: {
|
||||
locale: {
|
||||
currentLanguageTag: "fr"
|
||||
},
|
||||
// @ts-expect-error
|
||||
profile: {}
|
||||
},
|
||||
overridesPerPage: {
|
||||
"register.ftl": {
|
||||
profile: {
|
||||
attributesByName: {
|
||||
username: {
|
||||
validators: {
|
||||
pattern: {
|
||||
pattern: "^[a-zA-Z0-9]+$",
|
||||
"ignore.empty.value": true,
|
||||
"error-message": "${alphanumericalCharsOnly}"
|
||||
}
|
||||
},
|
||||
value: undefined,
|
||||
name: "username"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// @ts-expect-error
|
||||
"non-existing-page.ftl": {}
|
||||
}
|
||||
});
|
||||
|
||||
createGetKcContextMock({
|
||||
kcContextExtraProperties: Reflect<KcContextExtraProperties>(),
|
||||
kcContextExtraPropertiesPerPage: Reflect<KcContextExtraPropertiesPerPage>(),
|
||||
overridesPerPage: {
|
||||
"register.ftl": {
|
||||
// @ts-expect-error
|
||||
nonExistingProperty: 42
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
type KcContextExtraProperties = {};
|
||||
|
||||
type KcContextExtraPropertiesPerPage = {};
|
||||
|
||||
type KcContext = ExtendKcContext<
|
||||
KcContextExtraProperties,
|
||||
KcContextExtraPropertiesPerPage
|
||||
>;
|
||||
|
||||
{
|
||||
type Got = Extract<KcContext, { pageId: "login.ftl" }>;
|
||||
type Expected = KcContextBase.Login;
|
||||
|
||||
assert<Equals<Got, Expected>>();
|
||||
}
|
||||
|
||||
{
|
||||
type Got = Extract<KcContext, { pageId: "register.ftl" }>;
|
||||
type Expected = KcContextBase.Register;
|
||||
|
||||
assert<Equals<Got, Expected>>();
|
||||
}
|
||||
|
||||
const { getKcContextMock } = createGetKcContextMock({
|
||||
kcContextExtraProperties: Reflect<KcContextExtraProperties>(),
|
||||
kcContextExtraPropertiesPerPage: Reflect<KcContextExtraPropertiesPerPage>()
|
||||
});
|
||||
|
||||
{
|
||||
const got = getKcContextMock({
|
||||
pageId: "login.ftl"
|
||||
});
|
||||
|
||||
type Expected = Extract<KcContext, { pageId: "login.ftl" }>;
|
||||
|
||||
assert<Equals<typeof got, Expected>>();
|
||||
}
|
||||
|
||||
{
|
||||
const got = getKcContextMock({
|
||||
pageId: "register.ftl"
|
||||
});
|
||||
|
||||
type Expected = Extract<KcContext, { pageId: "register.ftl" }>;
|
||||
|
||||
assert<Equals<typeof got, Expected>>();
|
||||
}
|
||||
|
||||
getKcContextMock({
|
||||
// @ts-expect-error
|
||||
pageId: "non-existing-page.ftl"
|
||||
});
|
||||
|
||||
getKcContextMock({
|
||||
pageId: "login.ftl",
|
||||
overrides: {
|
||||
// @ts-expect-error
|
||||
bar: 42
|
||||
}
|
||||
});
|
||||
}
|
202
test/login/kcContextMock.spec.ts
Normal file
202
test/login/kcContextMock.spec.ts
Normal file
@ -0,0 +1,202 @@
|
||||
import { createGetKcContextMock, type Attribute } from "keycloakify/login";
|
||||
import { id } from "tsafe/id";
|
||||
import {
|
||||
kcContextMocks,
|
||||
kcContextCommonMock
|
||||
} from "keycloakify/login/kcContext/kcContextMocks";
|
||||
import { structuredCloneButFunctions } from "keycloakify/tools/structuredCloneButFunctions";
|
||||
import { expect, it, describe } from "vitest";
|
||||
|
||||
describe("createGetKcContextMock", () => {
|
||||
type KcContextExtraProperties = {
|
||||
properties: {
|
||||
MY_ENV_VAR?: string;
|
||||
};
|
||||
};
|
||||
|
||||
type KcContextExtraPropertiesPerPage = {
|
||||
"register.ftl": {
|
||||
authorizedMailDomains: string[];
|
||||
};
|
||||
"my-plugin-page.ftl": {
|
||||
aCustomValue: string;
|
||||
};
|
||||
};
|
||||
|
||||
const { getKcContextMock } = createGetKcContextMock({
|
||||
kcContextExtraProperties: id<KcContextExtraProperties>({
|
||||
properties: {
|
||||
MY_ENV_VAR: "my env var value"
|
||||
}
|
||||
}),
|
||||
kcContextExtraPropertiesPerPage: id<KcContextExtraPropertiesPerPage>({
|
||||
"register.ftl": {
|
||||
authorizedMailDomains: ["gmail.com", "hotmail.com"]
|
||||
},
|
||||
"my-plugin-page.ftl": {
|
||||
aCustomValue: "some value"
|
||||
}
|
||||
}),
|
||||
overrides: {
|
||||
locale: {
|
||||
currentLanguageTag: "fr"
|
||||
}
|
||||
},
|
||||
overridesPerPage: {
|
||||
"register.ftl": {
|
||||
profile: {
|
||||
attributesByName: {
|
||||
username: {
|
||||
validators: {
|
||||
pattern: {
|
||||
pattern: "^[a-zA-Z0-9]+$",
|
||||
"ignore.empty.value": true,
|
||||
"error-message": "${alphanumericalCharsOnly}"
|
||||
}
|
||||
},
|
||||
value: undefined,
|
||||
name: "username"
|
||||
}
|
||||
}
|
||||
},
|
||||
passwordPolicies: {
|
||||
length: 66
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("returns the proper mock for register.frl", () => {
|
||||
const got = getKcContextMock({
|
||||
pageId: "register.ftl",
|
||||
overrides: {
|
||||
profile: {
|
||||
attributesByName: {
|
||||
gender: id<Attribute>({
|
||||
validators: {
|
||||
options: {
|
||||
options: [
|
||||
"male",
|
||||
"female",
|
||||
"non-binary",
|
||||
"prefer-not-to-say"
|
||||
]
|
||||
}
|
||||
},
|
||||
displayName: "${gender}",
|
||||
annotations: {},
|
||||
required: true,
|
||||
readOnly: false,
|
||||
name: "gender"
|
||||
}),
|
||||
email: undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const expected = (() => {
|
||||
const out: any = structuredCloneButFunctions(
|
||||
kcContextMocks.find(({ pageId }) => pageId === "register.ftl")
|
||||
);
|
||||
|
||||
out.properties = {
|
||||
MY_ENV_VAR: "my env var value"
|
||||
};
|
||||
|
||||
out.authorizedMailDomains = ["gmail.com", "hotmail.com"];
|
||||
|
||||
out.locale.currentLanguageTag = "fr";
|
||||
|
||||
delete out.profile.attributesByName.email;
|
||||
|
||||
{
|
||||
const usernameAttribute = out.profile.attributesByName.username;
|
||||
|
||||
delete usernameAttribute.value;
|
||||
usernameAttribute.validators.pattern = {
|
||||
pattern: "^[a-zA-Z0-9]+$",
|
||||
"ignore.empty.value": true,
|
||||
"error-message": "${alphanumericalCharsOnly}"
|
||||
};
|
||||
}
|
||||
|
||||
out.profile.attributesByName.gender = {
|
||||
validators: {
|
||||
options: {
|
||||
options: ["male", "female", "non-binary", "prefer-not-to-say"]
|
||||
}
|
||||
},
|
||||
displayName: "${gender}",
|
||||
annotations: {},
|
||||
required: true,
|
||||
readOnly: false,
|
||||
name: "gender"
|
||||
};
|
||||
|
||||
(out.passwordPolicies ??= {}).length = 66;
|
||||
|
||||
return out;
|
||||
})();
|
||||
|
||||
expect(got).toEqual(expected);
|
||||
});
|
||||
|
||||
it("returns the proper mock plugin pages", () => {
|
||||
const got = getKcContextMock({
|
||||
pageId: "my-plugin-page.ftl",
|
||||
overrides: {
|
||||
locale: {
|
||||
currentLanguageTag: "en"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const expected = (() => {
|
||||
const out: any = structuredCloneButFunctions(kcContextCommonMock);
|
||||
|
||||
out.pageId = "my-plugin-page.ftl";
|
||||
|
||||
out.aCustomValue = "some value";
|
||||
|
||||
out.properties = {
|
||||
MY_ENV_VAR: "my env var value"
|
||||
};
|
||||
|
||||
out.locale.currentLanguageTag = "en";
|
||||
|
||||
return out;
|
||||
})();
|
||||
|
||||
expect(got).toEqual(expected);
|
||||
});
|
||||
|
||||
it("returns the proper mock for other pages", () => {
|
||||
const got = getKcContextMock({
|
||||
pageId: "login.ftl",
|
||||
overrides: {
|
||||
realm: {
|
||||
registrationAllowed: false
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const expected = (() => {
|
||||
const out: any = structuredCloneButFunctions(
|
||||
kcContextMocks.find(({ pageId }) => pageId === "login.ftl")
|
||||
);
|
||||
|
||||
out.properties = {
|
||||
MY_ENV_VAR: "my env var value"
|
||||
};
|
||||
|
||||
out.locale.currentLanguageTag = "fr";
|
||||
|
||||
out.realm.registrationAllowed = false;
|
||||
|
||||
return out;
|
||||
})();
|
||||
|
||||
expect(got).toEqual(expected);
|
||||
});
|
||||
});
|
@ -1,6 +0,0 @@
|
||||
export function isSameCode(code1: string, code2: string): boolean {
|
||||
const removeSpacesAndNewLines = (code: string) =>
|
||||
code.replace(/\s/g, "").replace(/\n/g, "");
|
||||
|
||||
return removeSpacesAndNewLines(code1) === removeSpacesAndNewLines(code2);
|
||||
}
|
Reference in New Issue
Block a user