Compare commits
19 Commits
Author | SHA1 | Date | |
---|---|---|---|
5cf290b033 | |||
aec3da25b3 | |||
66d7cb563d | |||
551e9c041e | |||
fffb6d5b5e | |||
ac0bfeb360 | |||
7c30059ca3 | |||
fdb9ae6c40 | |||
3c82ffc0ab | |||
5dd3103aba | |||
84fc81f531 | |||
a20cbc62a5 | |||
e6a93e2838 | |||
3cff54561f | |||
e50a6a7876 | |||
b887ec839b | |||
465daa19a0 | |||
6c2b761d95 | |||
0e8f95ce19 |
28
CHANGELOG.md
28
CHANGELOG.md
@ -1,3 +1,31 @@
|
|||||||
|
### **2.0.6** (2021-07-08)
|
||||||
|
|
||||||
|
- Merge pull request #18 from asashay/add-custom-props-to-theme-properties
|
||||||
|
|
||||||
|
Add possibility to add custom properties to theme.properties file
|
||||||
|
- add possibility to add custom properties to theme.properties file
|
||||||
|
|
||||||
|
### **2.0.5** (2021-07-05)
|
||||||
|
|
||||||
|
- Fix broken url for big stylesheet #16
|
||||||
|
|
||||||
|
### **2.0.4** (2021-07-03)
|
||||||
|
|
||||||
|
- Fix: #7
|
||||||
|
|
||||||
|
### **2.0.3** (2021-06-30)
|
||||||
|
|
||||||
|
- Escape double quote in ftl to js conversion #15
|
||||||
|
- Update readme
|
||||||
|
|
||||||
|
### **2.0.2** (2021-06-28)
|
||||||
|
|
||||||
|
- Updagte README for implementing non incuded pages
|
||||||
|
|
||||||
|
### **2.0.1** (2021-06-28)
|
||||||
|
|
||||||
|
- Update documentation for v2
|
||||||
|
|
||||||
# **2.0.0** (2021-06-28)
|
# **2.0.0** (2021-06-28)
|
||||||
|
|
||||||
- Fix last bugs before relasing v2
|
- Fix last bugs before relasing v2
|
||||||
|
58
README.md
58
README.md
@ -24,8 +24,6 @@
|
|||||||
- It's now possible to implement custom `.ftl` pages.
|
- It's now possible to implement custom `.ftl` pages.
|
||||||
- Support for Keycloak plugins that introduce non standard ftl values.
|
- Support for Keycloak plugins that introduce non standard ftl values.
|
||||||
(Like for example [this plugin](https://github.com/micedre/keycloak-mail-whitelisting) that define `authorizedMailDomains` in `register.ftl`).
|
(Like for example [this plugin](https://github.com/micedre/keycloak-mail-whitelisting) that define `authorizedMailDomains` in `register.ftl`).
|
||||||
|
|
||||||
**V2 is not yet documented, most users should stick with v1.x.x**
|
|
||||||
# Motivations
|
# Motivations
|
||||||
|
|
||||||
Keycloak provides [theme support](https://www.keycloak.org/docs/latest/server_development/#_themes) for web pages. This allows customizing the look and feel of end-user facing pages so they can be integrated with your applications.
|
Keycloak provides [theme support](https://www.keycloak.org/docs/latest/server_development/#_themes) for web pages. This allows customizing the look and feel of end-user facing pages so they can be integrated with your applications.
|
||||||
@ -64,7 +62,7 @@ If you already have a Keycloak custom theme, it can be easily ported to Keycloak
|
|||||||
- [How to use](#how-to-use)
|
- [How to use](#how-to-use)
|
||||||
- [Setting up the build tool](#setting-up-the-build-tool)
|
- [Setting up the build tool](#setting-up-the-build-tool)
|
||||||
- [Changing just the look of the default Keycloak theme](#changing-just-the-look-of-the-default-keycloak-theme)
|
- [Changing just the look of the default Keycloak theme](#changing-just-the-look-of-the-default-keycloak-theme)
|
||||||
- [Changing the look **and** feel](#changing-the-look-and-feel)
|
- [Advanced pages configuration](#advanced-pages-configuration)
|
||||||
- [Hot reload](#hot-reload)
|
- [Hot reload](#hot-reload)
|
||||||
- [Enable loading in a blink of an eye of login pages ⚡ (--external-assets)](#enable-loading-in-a-blink-of-an-eye-of-login-pages----external-assets)
|
- [Enable loading in a blink of an eye of login pages ⚡ (--external-assets)](#enable-loading-in-a-blink-of-an-eye-of-login-pages----external-assets)
|
||||||
- [Support for Terms and conditions](#support-for-terms-and-conditions)
|
- [Support for Terms and conditions](#support-for-terms-and-conditions)
|
||||||
@ -136,10 +134,12 @@ import { App } from "./<wherever>/App";
|
|||||||
import {
|
import {
|
||||||
KcApp,
|
KcApp,
|
||||||
defaultKcProps,
|
defaultKcProps,
|
||||||
kcContext
|
getKcContext
|
||||||
} from "keycloakify";
|
} from "keycloakify";
|
||||||
import { css } from "tss-react";
|
import { css } from "tss-react";
|
||||||
|
|
||||||
|
const { kcContext } = getKcContext();
|
||||||
|
|
||||||
const myClassName = css({ "color": "red" });
|
const myClassName = css({ "color": "red" });
|
||||||
|
|
||||||
reactDom.render(
|
reactDom.render(
|
||||||
@ -163,10 +163,12 @@ import { App } from "./<wherever>/App";
|
|||||||
import {
|
import {
|
||||||
KcApp,
|
KcApp,
|
||||||
defaultKcProps,
|
defaultKcProps,
|
||||||
kcContext
|
getKcContext
|
||||||
} from "keycloakify";
|
} from "keycloakify";
|
||||||
import { css } from "tss-react";
|
import { css } from "tss-react";
|
||||||
|
|
||||||
|
const { kcContext } = getKcContext();
|
||||||
|
|
||||||
const myClassName = css({ "color": "red" });
|
const myClassName = css({ "color": "red" });
|
||||||
|
|
||||||
reactDom.render(
|
reactDom.render(
|
||||||
@ -201,20 +203,25 @@ and the result you can expect:
|
|||||||
<img src="https://github.com/InseeFrLab/keycloakify/releases/download/v0.3.8/keycloakify_after.gif">
|
<img src="https://github.com/InseeFrLab/keycloakify/releases/download/v0.3.8/keycloakify_after.gif">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
### Changing the look **and** feel
|
### Advanced pages configuration
|
||||||
|
|
||||||
If you want to really re-implement the pages, the best approach is to
|
If you want to go beyond only customizing the CSS you can re-implement some of the
|
||||||
create your own version of the [`<KcApp />`](https://github.com/garronej/keycloakify/blob/develop/src/lib/components/KcApp.tsx).
|
pages or event add new ones.
|
||||||
Copy/past some of [the components](https://github.com/garronej/keycloakify/tree/develop/src/lib/components) provided by this module and start hacking around.
|
|
||||||
|
|
||||||
You can find an example of such customization [here](https://github.com/InseeFrLab/onyxia-ui/tree/master/src/app/components/KcApp).
|
If you want to go this way checkout the demo setup provided [here](https://github.com/garronej/keycloakify-demo-app/tree/look_and_feel).
|
||||||
|
If you prefer a real life example you can checkout [onyxia-web's source](https://github.com/InseeFrLab/onyxia-web/tree/main/src/app/components/KcApp).
|
||||||
|
The web app is in production [here](https://datalab.sspcloud.fr).
|
||||||
|
|
||||||
And you can test the result in production by trying the login register page of [Onyxia](https://datalab.sspcloud.fr)
|
Main takeaways are:
|
||||||
|
- You must declare your custom pages in the package.json. [example](https://github.com/garronej/keycloakify-demo-app/blob/4eb2a9f63e9823e653b2d439495bda55e5ecc134/package.json#L17-L22)
|
||||||
Note that you don’t have to re write **all** components, only the ones that you most need customized.
|
- (TS only) You must declare theses page in the type argument of the getter
|
||||||
Look at [here for example](https://github.com/InseeFrLab/onyxia-ui/blob/3bf18aa82b198fc6ba7998c30abf0a9ae54a58b1/src/app/components/KcApp/KcApp.tsx#L112-L120).
|
function for the `kcContext` in order to have the correct typings. [example](https://github.com/garronej/keycloakify-demo-app/blob/4eb2a9f63e9823e653b2d439495bda55e5ecc134/src/KcApp/kcContext.ts#L16-L21)
|
||||||
We want to have our very own login and register page, so we wrote customs [Login.tsx](https://github.com/InseeFrLab/onyxia-ui/blob/master/src/app/components/KcApp/Login.tsx) and [Register.txs](https://github.com/InseeFrLab/onyxia-ui/blob/master/src/app/components/KcApp/Register.tsx), we import them [here](https://github.com/InseeFrLab/onyxia-ui/blob/3bf18aa82b198fc6ba7998c30abf0a9ae54a58b1/src/app/components/KcApp/KcApp.tsx#L9-L10) and use them [here](https://github.com/InseeFrLab/onyxia-ui/blob/3bf18aa82b198fc6ba7998c30abf0a9ae54a58b1/src/app/components/KcApp/KcApp.tsx#L113-L114).
|
- (TS only) If you use Keycloak plugins that defines non standard `.ftl` values
|
||||||
We don't want to bother, however, customizing `login-reset-password.ftl`. We are fine using the component from [the default theme](https://github.com/InseeFrLab/onyxia-ui/blob/3bf18aa82b198fc6ba7998c30abf0a9ae54a58b1/src/app/components/KcApp/KcApp.tsx#L13) with just some [CSS customization](https://github.com/InseeFrLab/onyxia-ui/blob/3bf18aa82b198fc6ba7998c30abf0a9ae54a58b1/src/app/components/KcApp/KcApp.tsx#L103-L110).
|
(Like for example [this plugin](https://github.com/micedre/keycloak-mail-whitelisting)
|
||||||
|
that define `authorizedMailDomains` in `register.ftl`) you should
|
||||||
|
declare theses value to get the type. [example](https://github.com/garronej/keycloakify-demo-app/blob/4eb2a9f63e9823e653b2d439495bda55e5ecc134/src/KcApp/kcContext.ts#L6-L13)
|
||||||
|
- You should provide sample data for all the non standard value if you want to be able
|
||||||
|
to debug the page outside of keycloak. [example](https://github.com/garronej/keycloakify-demo-app/blob/4eb2a9f63e9823e653b2d439495bda55e5ecc134/src/KcApp/kcContext.ts#L28-L43)
|
||||||
|
|
||||||
WARNING: If you chose to go this way use:
|
WARNING: If you chose to go this way use:
|
||||||
```json
|
```json
|
||||||
@ -227,16 +234,19 @@ in your `package.json` instead of `^X.Y.Z`. A minor update of Keycloakify might
|
|||||||
### Hot reload
|
### Hot reload
|
||||||
|
|
||||||
Rebuild the theme each time you make a change to see the result is not practical.
|
Rebuild the theme each time you make a change to see the result is not practical.
|
||||||
If you want to test your login screens outside of Keycloak, in [storybook](https://storybook.js.org/)
|
If you want to test your login screens outside of Keycloak you can mock a given `kcContext`:
|
||||||
for example you can use `kcContextMocks`.
|
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
import {
|
import {
|
||||||
KcApp,
|
KcApp,
|
||||||
defaultKcProps,
|
defaultKcProps,
|
||||||
kcContextMocks
|
getKcContext
|
||||||
} from "keycloakify";
|
} from "keycloakify";
|
||||||
|
|
||||||
|
const { kcContext } = getKcContext({
|
||||||
|
"mockPageId": "login.ftl"
|
||||||
|
});
|
||||||
|
|
||||||
reactDom.render(
|
reactDom.render(
|
||||||
<KcApp
|
<KcApp
|
||||||
kcContext={kcContextMocks.kcLoginContext}
|
kcContext={kcContextMocks.kcLoginContext}
|
||||||
@ -278,16 +288,10 @@ Then to load your own therms of services using [like this](https://github.com/ga
|
|||||||
|
|
||||||
# Some pages still have the default theme. Why?
|
# Some pages still have the default theme. Why?
|
||||||
|
|
||||||
**NEW in v1.2 it is now much more easy to add support for custom pages since the
|
This project only support out of the box the most common user facing pages of Keycloak login.
|
||||||
Keycloak context is now automatically converted into a JavaScript object (kcContext).
|
|
||||||
In v2 (coming soon) it won't be required to fork for adding support for custom pages.**
|
|
||||||
|
|
||||||
This project only support the most common user facing pages of Keycloak login.
|
|
||||||
[Here](https://user-images.githubusercontent.com/6702424/116787906-227fe700-aaa7-11eb-92ee-22e7673717c2.png) is the complete list of pages (you get them after running `yarn test`)
|
[Here](https://user-images.githubusercontent.com/6702424/116787906-227fe700-aaa7-11eb-92ee-22e7673717c2.png) is the complete list of pages (you get them after running `yarn test`)
|
||||||
and [here](https://github.com/InseeFrLab/keycloakify/tree/main/src/lib/components) are the pages currently implemented by this module.
|
and [here](https://github.com/InseeFrLab/keycloakify/tree/main/src/lib/components) are the pages currently implemented by this module.
|
||||||
If you need to customize pages that are not supported yet you can submit an issue about it and wait for me get it implemented.
|
If you need to customize pages that are not supported yet or if you need to implement some non standard `.ftl` pages please refer to [Advanced pages configuration](#advanced-pages-configuration).
|
||||||
If you can't wait, PR are welcome! [Here](https://github.com/InseeFrLab/keycloakify/commit/0163459ad6b1ad0afcc34fae5f3cc28dbcf8b4a7) is the commit that adds support
|
|
||||||
for the `login-otp.ftl` page. You can use it as a model for implementing other pages.
|
|
||||||
|
|
||||||
# GitHub Actions
|
# GitHub Actions
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "keycloakify",
|
"name": "keycloakify",
|
||||||
"version": "2.0.0",
|
"version": "2.0.6",
|
||||||
"description": "Keycloak theme generator for Reacts app",
|
"description": "Keycloak theme generator for Reacts app",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -19,6 +19,7 @@ export function main() {
|
|||||||
console.log("🔏 Building the keycloak theme...⌚");
|
console.log("🔏 Building the keycloak theme...⌚");
|
||||||
|
|
||||||
const extraPagesId: string[] = (parsedPackageJson as any)["keycloakify"]?.["extraPages"] ?? [];
|
const extraPagesId: string[] = (parsedPackageJson as any)["keycloakify"]?.["extraPages"] ?? [];
|
||||||
|
const extraThemeProperties: string[] = (parsedPackageJson as any)["keycloakify"]?.["extraThemeProperties"] ?? [];
|
||||||
|
|
||||||
generateKeycloakThemeResources({
|
generateKeycloakThemeResources({
|
||||||
keycloakThemeBuildingDirPath,
|
keycloakThemeBuildingDirPath,
|
||||||
@ -55,7 +56,8 @@ export function main() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
})(),
|
})(),
|
||||||
extraPagesId
|
extraPagesId,
|
||||||
|
extraThemeProperties
|
||||||
});
|
});
|
||||||
|
|
||||||
const { jarFilePath } = generateJavaStackFiles({
|
const { jarFilePath } = generateJavaStackFiles({
|
||||||
|
@ -113,7 +113,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<#attempt>
|
<#attempt>
|
||||||
"${object?no_esc}"
|
"${object?replace('"', '\\"')?no_esc}"
|
||||||
<#recover>
|
<#recover>
|
||||||
/* couldn't convert into string non hash, non method, non boolean, non enumerable object */
|
/* couldn't convert into string non hash, non method, non boolean, non enumerable object */
|
||||||
undefined;
|
undefined;
|
||||||
|
@ -23,12 +23,13 @@ export function generateKeycloakThemeResources(
|
|||||||
//If urlOrigin is not undefined then it means --externals-assets
|
//If urlOrigin is not undefined then it means --externals-assets
|
||||||
urlOrigin: undefined | string;
|
urlOrigin: undefined | string;
|
||||||
extraPagesId: string[];
|
extraPagesId: string[];
|
||||||
|
extraThemeProperties: string[];
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
|
|
||||||
const {
|
const {
|
||||||
themeName, reactAppBuildDirPath, keycloakThemeBuildingDirPath,
|
themeName, reactAppBuildDirPath, keycloakThemeBuildingDirPath,
|
||||||
urlPathname, urlOrigin, extraPagesId
|
urlPathname, urlOrigin, extraPagesId, extraThemeProperties
|
||||||
} = params;
|
} = params;
|
||||||
|
|
||||||
const themeDirPath = pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", themeName, "login");
|
const themeDirPath = pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", themeName, "login");
|
||||||
@ -166,7 +167,10 @@ export function generateKeycloakThemeResources(
|
|||||||
|
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
pathJoin(themeDirPath, "theme.properties"),
|
pathJoin(themeDirPath, "theme.properties"),
|
||||||
Buffer.from("parent=keycloak", "utf8")
|
Buffer.from(
|
||||||
|
"parent=keycloak".concat("\n\n", extraThemeProperties.join("\n\n")),
|
||||||
|
"utf8"
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,18 +5,39 @@ import { ftlValuesGlobalName } from "./ftlValuesGlobalName";
|
|||||||
export function replaceImportsFromStaticInJsCode(
|
export function replaceImportsFromStaticInJsCode(
|
||||||
params: {
|
params: {
|
||||||
jsCode: string;
|
jsCode: string;
|
||||||
urlOrigin: undefined | string;
|
urlOrigin: undefined | string;
|
||||||
}
|
}
|
||||||
): { fixedJsCode: string; } {
|
): { fixedJsCode: string; } {
|
||||||
|
|
||||||
|
/*
|
||||||
|
NOTE:
|
||||||
|
|
||||||
|
When we have urlOrigin defined it means that
|
||||||
|
we are building with --external-assets
|
||||||
|
so we have to make sur that the fixed js code will run
|
||||||
|
inside and outside keycloak.
|
||||||
|
|
||||||
|
When urlOrigin isn't defined we can assume the fixedJsCode
|
||||||
|
will always run in keycloak context.
|
||||||
|
*/
|
||||||
|
|
||||||
const { jsCode, urlOrigin } = params;
|
const { jsCode, urlOrigin } = params;
|
||||||
|
|
||||||
const fixedJsCode = jsCode.replace(
|
const fixedJsCode =
|
||||||
|
jsCode
|
||||||
|
.replace(
|
||||||
/([a-z]+\.[a-z]+)\+"static\//g,
|
/([a-z]+\.[a-z]+)\+"static\//g,
|
||||||
(...[, group]) =>
|
(...[, group]) =>
|
||||||
urlOrigin === undefined ?
|
urlOrigin === undefined ?
|
||||||
`window.${ftlValuesGlobalName}.url.resourcesPath + "/build/static/` :
|
`window.${ftlValuesGlobalName}.url.resourcesPath + "/build/static/` :
|
||||||
`("${ftlValuesGlobalName}" in window ? "${urlOrigin}" : "") + ${group} + "static/`
|
`("${ftlValuesGlobalName}" in window ? "${urlOrigin}" : "") + ${group} + "static/`
|
||||||
|
)
|
||||||
|
.replace(
|
||||||
|
/".chunk.css",([a-z])+=([a-z]+\.[a-z]+)\+([a-z]+),/,
|
||||||
|
(...[, group1, group2, group3]) =>
|
||||||
|
urlOrigin === undefined ?
|
||||||
|
`".chunk.css",${group1} = window.${ftlValuesGlobalName}.url.resourcesPath + "/build/" + ${group3},` :
|
||||||
|
`".chunk.css",${group1} = ("${ftlValuesGlobalName}" in window ? "${urlOrigin}" : "") + ${group2} + ${group3},`
|
||||||
);
|
);
|
||||||
|
|
||||||
return { fixedJsCode };
|
return { fixedJsCode };
|
||||||
|
@ -8,9 +8,9 @@ import { deepAssign } from "../tools/deepAssign";
|
|||||||
|
|
||||||
|
|
||||||
export type ExtendsKcContextBase<
|
export type ExtendsKcContextBase<
|
||||||
KcContextExtended extends ({ pageId: string; } | undefined)
|
KcContextExtended extends { pageId: string; }
|
||||||
> =
|
> =
|
||||||
KcContextExtended extends undefined ?
|
[KcContextExtended] extends [never] ?
|
||||||
KcContextBase :
|
KcContextBase :
|
||||||
AndByDiscriminatingKey<
|
AndByDiscriminatingKey<
|
||||||
"pageId",
|
"pageId",
|
||||||
@ -18,7 +18,7 @@ export type ExtendsKcContextBase<
|
|||||||
KcContextBase
|
KcContextBase
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export function getKcContext<KcContextExtended extends ({ pageId: string; } | undefined) = undefined>(
|
export function getKcContext<KcContextExtended extends { pageId: string; } = never>(
|
||||||
params?: {
|
params?: {
|
||||||
mockPageId?: ExtendsKcContextBase<KcContextExtended>["pageId"];
|
mockPageId?: ExtendsKcContextBase<KcContextExtended>["pageId"];
|
||||||
mockData?: readonly DeepPartial<ExtendsKcContextBase<KcContextExtended>>[];
|
mockData?: readonly DeepPartial<ExtendsKcContextBase<KcContextExtended>>[];
|
||||||
@ -51,22 +51,15 @@ export function getKcContext<KcContextExtended extends ({ pageId: string; } | un
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const kcContext: any = { "pageId": mockPageId };
|
const kcContext: any = {};
|
||||||
|
|
||||||
deepAssign({
|
deepAssign({
|
||||||
"target": kcContext,
|
"target": kcContext,
|
||||||
"source": kcContextCommonMock
|
"source": kcContextDefaultMock !== undefined ?
|
||||||
|
kcContextDefaultMock :
|
||||||
|
{ "pageId": mockPageId, ...kcContextCommonMock, }
|
||||||
});
|
});
|
||||||
|
|
||||||
if (kcContextDefaultMock !== undefined) {
|
|
||||||
|
|
||||||
deepAssign({
|
|
||||||
"target": kcContext,
|
|
||||||
"source": kcContextDefaultMock
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (partialKcContextCustomMock !== undefined) {
|
if (partialKcContextCustomMock !== undefined) {
|
||||||
|
|
||||||
deepAssign({
|
deepAssign({
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
import { assert } from "tsafe/assert";
|
import { assert } from "tsafe/assert";
|
||||||
import { is } from "tsafe/is";
|
import { is } from "tsafe/is";
|
||||||
|
|
||||||
|
//Warning: Be mindful that because of array this is not idempotent.
|
||||||
export function deepAssign(
|
export function deepAssign(
|
||||||
params: {
|
params: {
|
||||||
target: Record<string, unknown>;
|
target: Record<string, unknown>;
|
||||||
|
@ -14,6 +14,7 @@ generateKeycloakThemeResources({
|
|||||||
"keycloakThemeBuildingDirPath": pathJoin(sampleReactProjectDirPath, "build_keycloak_theme"),
|
"keycloakThemeBuildingDirPath": pathJoin(sampleReactProjectDirPath, "build_keycloak_theme"),
|
||||||
"urlPathname": "/keycloakify-demo-app/",
|
"urlPathname": "/keycloakify-demo-app/",
|
||||||
"urlOrigin": undefined,
|
"urlOrigin": undefined,
|
||||||
"extraPagesId": ["my-custom-page.ftl"]
|
"extraPagesId": ["my-custom-page.ftl"],
|
||||||
|
"extraThemeProperties": ["env=test"]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -6,9 +6,10 @@ import { same } from "evt/tools/inDepth";
|
|||||||
import { doExtends } from "tsafe/doExtends";
|
import { doExtends } from "tsafe/doExtends";
|
||||||
import { assert } from "tsafe/assert";
|
import { assert } from "tsafe/assert";
|
||||||
import { kcContextMocks, kcContextCommonMock } from "../../lib/getKcContext/kcContextMocks";
|
import { kcContextMocks, kcContextCommonMock } from "../../lib/getKcContext/kcContextMocks";
|
||||||
import { deepClone } from "../../lib/tools/deepClone";
|
import { deepClone } from "../../lib/tools/deepClone";
|
||||||
import type { Any } from "ts-toolbelt";
|
import type { Any } from "ts-toolbelt";
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
const authorizedMailDomains = [
|
const authorizedMailDomains = [
|
||||||
"example.com",
|
"example.com",
|
||||||
@ -20,23 +21,27 @@ const authorizedMailDomains = [
|
|||||||
|
|
||||||
const displayName = "this is an overwritten common value";
|
const displayName = "this is an overwritten common value";
|
||||||
|
|
||||||
const aNonStandardValue = "a non standard value";
|
const aNonStandardValue1 = "a non standard value 1";
|
||||||
|
const aNonStandardValue2 = "a non standard value 2";
|
||||||
|
|
||||||
type KcContextExtended = {
|
type KcContextExtended = {
|
||||||
pageId: "register.ftl";
|
pageId: "register.ftl";
|
||||||
authorizedMailDomains: string[];
|
authorizedMailDomains: string[];
|
||||||
|
} | {
|
||||||
|
pageId: "info.ftl";
|
||||||
|
aNonStandardValue1: string;
|
||||||
} | {
|
} | {
|
||||||
pageId: "my-extra-page-1.ftl";
|
pageId: "my-extra-page-1.ftl";
|
||||||
} | {
|
} | {
|
||||||
pageId: "my-extra-page-2.ftl";
|
pageId: "my-extra-page-2.ftl";
|
||||||
aNonStandardValue: string;
|
aNonStandardValue2: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
function getKcContextProxy(
|
const getKcContextProxy = (
|
||||||
params: {
|
params: {
|
||||||
mockPageId: ExtendsKcContextBase<KcContextExtended>["pageId"];
|
mockPageId: ExtendsKcContextBase<KcContextExtended>["pageId"];
|
||||||
}
|
}
|
||||||
) {
|
) => {
|
||||||
|
|
||||||
const { mockPageId } = params;
|
const { mockPageId } = params;
|
||||||
|
|
||||||
@ -47,20 +52,24 @@ function getKcContextProxy(
|
|||||||
"pageId": "login.ftl",
|
"pageId": "login.ftl",
|
||||||
"realm": { displayName }
|
"realm": { displayName }
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"pageId": "info.ftl",
|
||||||
|
aNonStandardValue1
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"pageId": "register.ftl",
|
"pageId": "register.ftl",
|
||||||
authorizedMailDomains
|
authorizedMailDomains
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"pageId": "my-extra-page-2.ftl",
|
"pageId": "my-extra-page-2.ftl",
|
||||||
aNonStandardValue
|
aNonStandardValue2
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
return { kcContext };
|
return { kcContext };
|
||||||
|
|
||||||
}
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -68,12 +77,9 @@ function getKcContextProxy(
|
|||||||
|
|
||||||
const { kcContext } = getKcContextProxy({ "mockPageId": pageId });
|
const { kcContext } = getKcContextProxy({ "mockPageId": pageId });
|
||||||
|
|
||||||
//@ts-expect-error
|
|
||||||
doExtends<Any.Equals<typeof kcContext, any>, 1>();
|
|
||||||
|
|
||||||
assert(kcContext?.pageId === pageId);
|
assert(kcContext?.pageId === pageId);
|
||||||
|
|
||||||
doExtends<typeof kcContext, KcContextBase.Login>();
|
doExtends<Any.Equals<typeof kcContext, KcContextBase.Login>, 1>();
|
||||||
|
|
||||||
assert(same(
|
assert(same(
|
||||||
//NOTE: deepClone for printIfExists or other functions...
|
//NOTE: deepClone for printIfExists or other functions...
|
||||||
@ -93,17 +99,42 @@ function getKcContextProxy(
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
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; ...
|
||||||
|
doExtends<Any.Equals<typeof kcContext, KcContextBase.Info & { pageId: typeof pageId; aNonStandardValue1: string; }>, 1>();
|
||||||
|
|
||||||
|
assert(same(
|
||||||
|
deepClone(kcContext),
|
||||||
|
(() => {
|
||||||
|
|
||||||
|
const mock = deepClone(kcContextMocks.find(({ pageId: pageId_i }) => pageId_i === pageId)!);
|
||||||
|
|
||||||
|
Object.assign(mock, { aNonStandardValue1 });
|
||||||
|
|
||||||
|
return mock;
|
||||||
|
|
||||||
|
})()
|
||||||
|
));
|
||||||
|
|
||||||
|
console.log(`PASS ${pageId}`);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const pageId = "register.ftl";
|
const pageId = "register.ftl";
|
||||||
|
|
||||||
const { kcContext } = getKcContextProxy({ "mockPageId": pageId });
|
const { kcContext } = getKcContextProxy({ "mockPageId": pageId });
|
||||||
|
|
||||||
//@ts-expect-error
|
|
||||||
doExtends<Any.Equals<typeof kcContext, any>, 1>();
|
|
||||||
|
|
||||||
assert(kcContext?.pageId === pageId);
|
assert(kcContext?.pageId === pageId);
|
||||||
|
|
||||||
doExtends<typeof kcContext, KcContextBase.Register>();
|
//NOTE: I don't understand the need to add: pageId: typeof pageId; ...
|
||||||
|
doExtends<Any.Equals<typeof kcContext, KcContextBase.Register & { pageId: typeof pageId; authorizedMailDomains: string[]; }>, 1>();
|
||||||
|
|
||||||
assert(same(
|
assert(same(
|
||||||
deepClone(kcContext),
|
deepClone(kcContext),
|
||||||
@ -128,15 +159,11 @@ function getKcContextProxy(
|
|||||||
|
|
||||||
const { kcContext } = getKcContextProxy({ "mockPageId": pageId });
|
const { kcContext } = getKcContextProxy({ "mockPageId": pageId });
|
||||||
|
|
||||||
//@ts-expect-error
|
|
||||||
doExtends<Any.Equals<typeof kcContext, any>, 1>();
|
|
||||||
|
|
||||||
assert(kcContext?.pageId === pageId);
|
assert(kcContext?.pageId === pageId);
|
||||||
|
|
||||||
//@ts-expect-error
|
doExtends<Any.Equals<typeof kcContext, KcContextBase.Common & { pageId: typeof pageId; aNonStandardValue2: string; }>, 1>();
|
||||||
doExtends<typeof kcContext, KcContextBase>();
|
|
||||||
|
|
||||||
doExtends<typeof kcContext, KcContextBase.Common>();
|
kcContext.aNonStandardValue2;
|
||||||
|
|
||||||
assert(same(
|
assert(same(
|
||||||
deepClone(kcContext),
|
deepClone(kcContext),
|
||||||
@ -144,7 +171,7 @@ function getKcContextProxy(
|
|||||||
|
|
||||||
const mock = deepClone(kcContextCommonMock);
|
const mock = deepClone(kcContextCommonMock);
|
||||||
|
|
||||||
Object.assign(mock, { pageId, aNonStandardValue });
|
Object.assign(mock, { pageId, aNonStandardValue2 });
|
||||||
|
|
||||||
return mock;
|
return mock;
|
||||||
|
|
||||||
@ -163,15 +190,10 @@ function getKcContextProxy(
|
|||||||
|
|
||||||
const { kcContext } = getKcContextProxy({ "mockPageId": pageId });
|
const { kcContext } = getKcContextProxy({ "mockPageId": pageId });
|
||||||
|
|
||||||
//@ts-expect-error
|
|
||||||
doExtends<Any.Equals<typeof kcContext, any>, 1>();
|
|
||||||
|
|
||||||
assert(kcContext?.pageId === pageId);
|
assert(kcContext?.pageId === pageId);
|
||||||
|
|
||||||
//@ts-expect-error
|
doExtends<Any.Equals<typeof kcContext, KcContextBase.Common & { pageId: typeof pageId; }>, 1>();
|
||||||
doExtends<typeof kcContext, KcContextBase>();
|
|
||||||
|
|
||||||
doExtends<typeof kcContext, KcContextBase.Common>();
|
|
||||||
|
|
||||||
assert(same(
|
assert(same(
|
||||||
deepClone(kcContext),
|
deepClone(kcContext),
|
||||||
@ -190,18 +212,39 @@ function getKcContextProxy(
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
const pageId = "login.ftl";
|
||||||
|
|
||||||
|
const { kcContext } = getKcContext({
|
||||||
|
"mockPageId": pageId
|
||||||
|
});
|
||||||
|
|
||||||
|
doExtends<Any.Equals<typeof kcContext, KcContextBase | undefined>, 1>();
|
||||||
|
|
||||||
|
assert(same(
|
||||||
|
deepClone(kcContext),
|
||||||
|
deepClone(kcContextMocks.find(({ pageId: pageId_i }) => pageId_i === pageId)!)
|
||||||
|
));
|
||||||
|
|
||||||
|
console.log("PASS no extension");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
const { kcContext } = getKcContext();
|
const { kcContext } = getKcContext();
|
||||||
|
|
||||||
//@ts-expect-error
|
doExtends<Any.Equals<typeof kcContext, KcContextBase | undefined>, 1>();
|
||||||
doExtends<Any.Equals<typeof kcContext, any>, 1>();
|
|
||||||
|
|
||||||
doExtends<typeof kcContext, KcContextBase | undefined>();
|
assert(kcContext === undefined);
|
||||||
|
|
||||||
|
console.log("PASS no extension, no mock");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user