diff --git a/docs/.astro/types.d.ts b/docs/.astro/types.d.ts
index 1190ddfc..ccc2a977 100644
--- a/docs/.astro/types.d.ts
+++ b/docs/.astro/types.d.ts
@@ -297,6 +297,13 @@ declare module 'astro:content' {
collection: "docs";
data: InferEntrySchema<"docs">
} & { render(): Render[".mdx"] };
+"reference/testing.mdx": {
+ id: "reference/testing.mdx";
+ slug: "reference/testing";
+ body: string;
+ collection: "docs";
+ data: InferEntrySchema<"docs">
+} & { render(): Render[".mdx"] };
"reference/theming.mdx": {
id: "reference/theming.mdx";
slug: "reference/theming";
diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs
index 2173934f..eed51f64 100644
--- a/docs/astro.config.mjs
+++ b/docs/astro.config.mjs
@@ -122,6 +122,11 @@ export default defineConfig({
label: 'Debugging',
link: '/reference/debugging/'
},
+ {
+ label: 'Testing',
+ link: '/reference/testing/',
+ badge: 'New'
+ },
{
label: 'Errors',
link: '/reference/errors/'
diff --git a/docs/src/content/docs/reference/testing.mdx b/docs/src/content/docs/reference/testing.mdx
new file mode 100644
index 00000000..d8868073
--- /dev/null
+++ b/docs/src/content/docs/reference/testing.mdx
@@ -0,0 +1,66 @@
+---
+title: Testing
+---
+
+import Seo from '../../../components/Seo.astro'
+import Badge from '../../../components/Badge.astro'
+
+
+
+
+
+
+Unistyles ships with own mocks that will help you to test your components that consume `useStyles` and `createStyleSheet`.
+
+### Using mocks
+
+In order to use the library mocks you need to either use `jest` or set `process.env.NODE_ENV` to `test`.
+
+```json /NODE_ENV=test/
+"scripts": {
+ "start": "react-native start",
+ "test": "NODE_ENV=test vitest"
+},
+```
+
+### NativeEventEmitter
+
+To mock `NativeEventEmitter` that is imported internally from `react-native` you can use the following code:
+
+1. Create `jest.setup.js` file:
+
+```javascript /NativeEventEmitter/
+jest.mock('react-native/Libraries/EventEmitter/NativeEventEmitter');
+```
+
+2. If you use `jest.config.js`:
+
+```diff lang="js"
+module.exports = {
+ preset: 'react-native',
++ setupFiles: ['./jest.setup.js'],
+};
+```
+
+3. If you use `jest` in `package.json`:
+
+```diff lang="json"
+{
+ "jest": {
+ "preset": "react-native",
++ "setupFiles": ["./jest.setup.js"]
+ }
+}
+```
+
+### Support
+
+Mocks support basic operations and are designed to mimic the setting of themes and breakpoints.
+However, some operations, such as using `plugins` or `adaptiveThemes`, are not supported.
+
+
diff --git a/src/common.ts b/src/common.ts
index 06714935..c71955e5 100644
--- a/src/common.ts
+++ b/src/common.ts
@@ -10,6 +10,7 @@ export const isAndroid = Platform.OS === 'android'
export const isMobile = isIOS || isAndroid
export const isServer = typeof window === 'undefined'
export const isDev = process.env.NODE_ENV !== 'production'
+export const isTest = process.env.NODE_ENV === 'test' || process.env.JEST_WORKER_ID !== undefined || typeof jest !== 'undefined'
export const ScreenOrientation = {
Landscape: 'landscape',
diff --git a/src/core/Unistyles.ts b/src/core/Unistyles.ts
index b1b3dc06..21065c06 100644
--- a/src/core/Unistyles.ts
+++ b/src/core/Unistyles.ts
@@ -2,7 +2,8 @@ import { UnistylesModule } from './UnistylesModule'
import { UnistylesRuntime } from './UnistylesRuntime'
import { UnistyleRegistry } from './UnistyleRegistry'
import type { UnistylesBridge } from '../types'
-import { UnistylesError, isWeb } from '../common'
+import { UnistylesError, isTest, isWeb } from '../common'
+import { UnistylesMockedBridge, UnistylesMockedRegistry, UnistylesMockedRuntime } from './mocks'
class Unistyles {
private _runtime: UnistylesRuntime
@@ -10,6 +11,14 @@ class Unistyles {
private _bridge: UnistylesBridge
constructor() {
+ if (isTest) {
+ this._bridge = new UnistylesMockedBridge() as unknown as UnistylesBridge
+ this._registry = new UnistylesMockedRegistry(this._bridge) as unknown as UnistyleRegistry
+ this._runtime = new UnistylesMockedRuntime(this._bridge, this._registry) as unknown as UnistylesRuntime
+
+ return
+ }
+
const isInstalled = UnistylesModule?.install() ?? false
if (!isInstalled) {
diff --git a/src/core/mocks/UnistylesMockedBridge.ts b/src/core/mocks/UnistylesMockedBridge.ts
new file mode 100644
index 00000000..d167a5ad
--- /dev/null
+++ b/src/core/mocks/UnistylesMockedBridge.ts
@@ -0,0 +1,26 @@
+// @ts-nocheck
+import type { UnistylesThemes, UnistylesBreakpoints } from '../../global'
+
+export class UnistylesMockedBridge {
+ constructor() {}
+
+ public screenWidth() {}
+ public screenHeight() {}
+ public enabledPlugins() {}
+ public hasAdaptiveThemes() {}
+ public themeName() {}
+ public breakpoint() {}
+ public colorScheme() {}
+ public contentSizeCategory() {}
+ public sortedBreakpointPairs() {}
+ public insets() {}
+ public statusBar() {}
+ public navigationBar() {}
+ public themes(themes: Array) {}
+ public useBreakpoints(breakpoints: UnistylesBreakpoints) {}
+ public useTheme(name: keyof UnistylesThemes) {}
+ public updateTheme(name: keyof UnistylesThemes) {}
+ public useAdaptiveThemes(enable: boolean) {}
+ public addPlugin(pluginName: string, notify: boolean) {}
+ public removePlugin(pluginName: string) {}
+}
diff --git a/src/core/mocks/UnistylesMockedRegistry.ts b/src/core/mocks/UnistylesMockedRegistry.ts
new file mode 100644
index 00000000..2552e60d
--- /dev/null
+++ b/src/core/mocks/UnistylesMockedRegistry.ts
@@ -0,0 +1,47 @@
+// @ts-nocheck
+import type { UnistylesBreakpoints, UnistylesThemes } from '../../global'
+import type { UnistylesPlugin, UnistylesConfig, UnistylesBridge } from '../../types'
+
+export class UnistylesMockedRegistry {
+ public config: UnistylesConfig = {}
+ public breakpoints: UnistylesBreakpoints = {} as UnistylesBreakpoints
+ public sortedBreakpointPairs: Array<[keyof UnistylesBreakpoints, UnistylesBreakpoints[keyof UnistylesBreakpoints]]> = []
+ public plugins: Array = []
+ public themes: UnistylesThemes = {} as UnistylesThemes
+ public themeNames: Array = []
+
+ constructor(private unistylesBridge: UnistylesBridge) {}
+
+ public addThemes = (themes: UnistylesThemes) => {
+ this.themes = themes
+ this.themeNames = Object.keys(themes) as Array
+
+ return this
+ }
+ public addBreakpoints = (breakpoints: UnistylesBreakpoints) => {
+ this.breakpoints = breakpoints
+ this.sortedBreakpointPairs = Object
+ .entries(breakpoints)
+ .sort((breakpoint1, breakpoint2) => {
+ const [, value1] = breakpoint1
+ const [, value2] = breakpoint2
+
+ return value1 - value2
+ }) as Array<[keyof UnistylesBreakpoints, UnistylesBreakpoints[keyof UnistylesBreakpoints]]>
+
+ return this
+ }
+ public addConfig = (config: UnistylesConfig) => {}
+ public getTheme = (forName: keyof UnistylesThemes) => {
+ if (this.themeNames.length === 0) {
+ return {} as UnistylesThemes[keyof UnistylesThemes]
+ }
+
+ return this.themes[forName]
+ }
+
+ public addPlugin = (plugin: UnistylesPlugin, notify: boolean = true) => {}
+ public removePlugin = (plugin: UnistylesPlugin) => {}
+ public updateTheme = (name: keyof UnistylesThemes, theme: UnistylesThemes[keyof UnistylesThemes]) => {}
+ public hasTheme = (name: keyof UnistylesThemes) => true
+}
diff --git a/src/core/mocks/UnistylesMockedRuntime.ts b/src/core/mocks/UnistylesMockedRuntime.ts
new file mode 100644
index 00000000..b76e20ba
--- /dev/null
+++ b/src/core/mocks/UnistylesMockedRuntime.ts
@@ -0,0 +1,92 @@
+// @ts-nocheck
+import { ScreenOrientation } from '../../common'
+import type { UnistylesBridge, UnistylesPlugin } from '../../types'
+import type { UnistylesThemes } from '../../global'
+import type { UnistylesMockedRegistry } from './UnistylesMockedRegistry'
+import type { UnistyleRegistry } from '../UnistyleRegistry'
+
+export class UnistylesMockedRuntime {
+ private unistylesRegistry: UnistylesMockedRegistry
+
+ constructor(private unistylesBridge: UnistylesBridge, private unistylesRegistry: UnistyleRegistry) {
+ this.unistylesRegistry = unistylesRegistry as unknown as UnistylesMockedRegistry
+ }
+
+ public get colorScheme() {
+ return 'dark'
+ }
+
+ public get hasAdaptiveThemes() {
+ return true
+ }
+
+ public get themeName() {
+ return this.unistylesRegistry.themeNames.length > 0
+ ? this.unistylesRegistry.themeNames.at(0)
+ : undefined
+ }
+
+ public get contentSizeCategory() {
+ return 'unspecified'
+ }
+
+ public get breakpoint() {
+ if (this.unistylesRegistry.sortedBreakpointPairs.length === 0) {
+ return undefined
+ }
+
+ const firstBreakpoint = this.unistylesRegistry.sortedBreakpointPairs.at(0)
+
+ return firstBreakpoint
+ ? firstBreakpoint.at(0)
+ : undefined
+ }
+
+ public get breakpoints() {
+ return this.unistylesRegistry.breakpoints
+ }
+
+ public get enabledPlugins() {
+ return this.unistylesRegistry.plugins
+ }
+
+ public get screen() {
+ return {
+ width: 360,
+ height: 800
+ }
+ }
+
+ public get insets() {
+ return {
+ top: 0,
+ right: 0,
+ bottom: 0,
+ left: 0
+ }
+ }
+
+ public get statusBar() {
+ return {
+ height: 24,
+ width: 800
+ }
+ }
+
+ public get navigationBar() {
+ return {
+ height: 0,
+ width: 0
+ }
+ }
+
+ public get orientation() {
+ return ScreenOrientation.Portrait
+ }
+
+ public setTheme = (name: keyof UnistylesThemes) => true
+ public updateTheme = (name: keyof UnistylesThemes, theme: UnistylesThemes[keyof UnistylesThemes]) => {}
+ public setAdaptiveThemes = (enabled: boolean) => {}
+ public addPlugin = (plugin: UnistylesPlugin) => {}
+ public removePlugin = (plugin: UnistylesPlugin) => {}
+}
diff --git a/src/core/mocks/index.ts b/src/core/mocks/index.ts
new file mode 100644
index 00000000..5af4f25a
--- /dev/null
+++ b/src/core/mocks/index.ts
@@ -0,0 +1,3 @@
+export { UnistylesMockedBridge } from './UnistylesMockedBridge'
+export { UnistylesMockedRegistry } from './UnistylesMockedRegistry'
+export { UnistylesMockedRuntime } from './UnistylesMockedRuntime'