Skip to content

Commit

Permalink
Implement a utility for PackageIntl creation during tests (#34)
Browse files Browse the repository at this point in the history
  • Loading branch information
mbeckem authored Feb 13, 2024
1 parent f749d96 commit 2d5a94a
Show file tree
Hide file tree
Showing 8 changed files with 143 additions and 43 deletions.
5 changes: 5 additions & 0 deletions .changeset/eight-bears-draw.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@open-pioneer/test-utils": minor
---

Provide `createIntl` from `@open-pioneer/test-utils/vanilla` to make creation of a `PackageIntl` instance easier.
16 changes: 15 additions & 1 deletion src/packages/test-utils/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# @open-pioneer/test-utils
watch# @open-pioneer/test-utils

This package contains test utilities that make it easier to test parts of a pioneer application.

Expand Down Expand Up @@ -112,6 +112,20 @@ it("creates a new service instance with the defined references", async () => {
});
```

## Utilities for Vanilla JS

The function `createIntl` from `@open-pioneer/test-utils/vanilla` can be used to make the creation of an `intl` object easier from vanilla JavaScript:

```js
import { createIntl } from "@open-pioneer/test-utils/vanilla";

// In your test:
const intl = createIntl(/* optional arguments such as messages or locale */);
intl.formatMessage({
/* ... */
});
```

## License

Apache-2.0 (see `LICENSE` file)
2 changes: 1 addition & 1 deletion src/packages/test-utils/build.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
import { defineBuildConfig } from "@open-pioneer/build-support";

export default defineBuildConfig({
entryPoints: ["web-components", "react", "services"]
entryPoints: ["vanilla", "web-components", "react", "services"]
});
20 changes: 7 additions & 13 deletions src/packages/test-utils/react.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
// SPDX-FileCopyrightText: 2023 Open Pioneer project (https://github.com/open-pioneer)
// SPDX-License-Identifier: Apache-2.0
import { createIntl, createIntlCache, IntlShape } from "@formatjs/intl";
import { CustomChakraProvider } from "@open-pioneer/chakra-integration";
import type { Service } from "@open-pioneer/runtime";
import type { PackageIntl, Service } from "@open-pioneer/runtime";
import {
PackageContext as InternalPackageContext,
PackageContextMethods
} from "@open-pioneer/runtime-react-support";
import { FC, ReactNode, useMemo } from "react";
import { INTL_ERROR_HANDLER } from "./utils";
import { createIntl } from "./vanilla";

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type AnyService = Service<any>;
Expand Down Expand Up @@ -80,7 +79,7 @@ function createPackageContextMethods(
const properties = options?.properties ?? {};
const locale = options?.locale ?? "en";
const messages = options?.messages ?? {};
const cachedIntl: Record<string, IntlShape> = {};
const cachedIntl: Record<string, PackageIntl> = {};
return {
getService(packageName, interfaceName, options) {
if (!options.qualifier) {
Expand Down Expand Up @@ -127,15 +126,10 @@ function createPackageContextMethods(
getIntl(packageName) {
const initIntl = () => {
const packageMessages = messages[packageName];
const cache = createIntlCache();
return createIntl(
{
locale,
messages: packageMessages,
onError: INTL_ERROR_HANDLER
},
cache
);
return createIntl({
locale,
messages: packageMessages
});
};
return (cachedIntl[packageName] ??= initIntl());
}
Expand Down
18 changes: 2 additions & 16 deletions src/packages/test-utils/services.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
// SPDX-FileCopyrightText: 2023 Open Pioneer project (https://github.com/open-pioneer)
// SPDX-License-Identifier: Apache-2.0
import { ReferenceMeta, Service, ServiceConstructor } from "@open-pioneer/runtime";
import { createIntl, createIntlCache } from "@formatjs/intl";
import { INTL_ERROR_HANDLER } from "./utils";
import { createIntl } from "./vanilla";

/**
* Options for the {@link createService} function.
Expand Down Expand Up @@ -83,20 +82,7 @@ export async function createService<References extends {}, Interface extends {}>
])
);

const locale = options?.locale ?? "en";
const defaultMessageLocale = options?.defaultMessageLocale ?? "en";
const messages = options?.messages ?? {};
const cache = createIntlCache();
const intl = createIntl(
{
locale,
defaultLocale: defaultMessageLocale,
messages,
onError: INTL_ERROR_HANDLER
},
cache
);

const intl = createIntl(options);
return new clazz({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
references: references as any,
Expand Down
12 changes: 0 additions & 12 deletions src/packages/test-utils/utils.ts

This file was deleted.

41 changes: 41 additions & 0 deletions src/packages/test-utils/vanilla.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// SPDX-FileCopyrightText: 2023 Open Pioneer project (https://github.com/open-pioneer)
// SPDX-License-Identifier: Apache-2.0
import { expect, it, afterEach, vi } from "vitest";
import { createIntl } from "./vanilla";

afterEach(() => {
vi.restoreAllMocks();
});

it("creates an intl object", () => {
const intl = createIntl();
expect(intl.locale).toBe("en");
});

it("creates an intl object with custom locale", () => {
const intl = createIntl({
locale: "de"
});
expect(intl.locale).toBe("de");
});

it("creates an intl object with custom messages", () => {
const intl = createIntl({
messages: {
"foo.bar": "baz"
}
});
const message = intl.formatMessage({ id: "foo.bar" });
expect(message).toBe("baz");
});

it("renders the message id and does not warn if a message is not defined", () => {
const spy = vi.spyOn(console, "error").mockImplementation(() => undefined);

const intl = createIntl({
messages: {}
});
const message = intl.formatMessage({ id: "foo.bar" });
expect(message).toBe("foo.bar");
expect(spy).not.toHaveBeenCalled();
});
72 changes: 72 additions & 0 deletions src/packages/test-utils/vanilla.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// SPDX-FileCopyrightText: 2023 Open Pioneer project (https://github.com/open-pioneer)
// SPDX-License-Identifier: Apache-2.0
import {
createIntlCache,
createIntl as createFormatJsIntl,
IntlErrorCode,
OnErrorFn
} from "@formatjs/intl";
import { PackageIntl } from "@open-pioneer/runtime";

/** Options for `createIntl`. */
export interface I18nOptions {
/**
* The locale for i18n messages and formatting.
*
* @default "en"
*/
locale?: string;

/**
* The locale for embedded default messages (e.g. `defaultMessage` property in `intl.formatMessage(...)`).
* It is usually not necessary to specify this option.
*
* See also https://formatjs.io/docs/intl#message-descriptor
*
* @default "en"
*/
defaultMessageLocale?: string;

/**
* I18n messages as (messageId, message) entries.
*
* @default {}
*/
messages?: Record<string, string>;
}

/**
* Creates an `intl` instance that can be used for testing.
*
* Other than the default implementation provided by `@formatjs/intl`, this
* `intl` object will not warn if a message is not defined.
* Instead, it will simply render the fallback message.
* This behavior makes testing easier.
*
* Note that messages can still be defined by using the `messages` parameter.
*/
export function createIntl(options?: I18nOptions): PackageIntl {
const messages = options?.messages ?? {};
const locale = options?.locale ?? "en";
const defaultMessageLocale = options?.defaultMessageLocale ?? "en";
const cache = createIntlCache();
const intl = createFormatJsIntl(
{
locale,
defaultLocale: defaultMessageLocale,
messages,
onError: INTL_ERROR_HANDLER
},
cache
);
return intl;
}

/** Hides missing translation errors during tests */
const INTL_ERROR_HANDLER: OnErrorFn = (err) => {
if (err.code === IntlErrorCode.MISSING_TRANSLATION) {
return;
}

console.error(err);
};

0 comments on commit 2d5a94a

Please sign in to comment.