@stone-js/starters 0.8.13 → 0.8.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/basic-react-declarative/package.json +6 -5
- package/basic-react-declarative/tests/Application.spec.ts +4 -3
- package/basic-react-imperative/package.json +6 -5
- package/basic-react-imperative/tests/Application.spec.ts +4 -3
- package/basic-react-native-declarative/README.md +15 -22
- package/basic-react-native-declarative/package.json +7 -7
- package/basic-react-native-declarative/tests/HomeScreen.spec.ts +36 -0
- package/basic-react-native-declarative/tests/{Application.spec.ts → navigation.spec.ts} +10 -7
- package/basic-react-native-declarative/vitest.config.ts +6 -0
- package/basic-react-native-imperative/README.md +15 -22
- package/basic-react-native-imperative/package.json +7 -7
- package/basic-react-native-imperative/tests/HomeScreen.spec.ts +36 -0
- package/basic-react-native-imperative/tests/{Application.spec.ts → navigation.spec.ts} +10 -7
- package/basic-react-native-imperative/vitest.config.ts +6 -0
- package/basic-service-declarative/package.json +5 -4
- package/basic-service-declarative/tests/Application.spec.ts +2 -1
- package/basic-service-imperative/package.json +5 -4
- package/basic-service-imperative/tests/Application.spec.ts +2 -1
- package/continuum-showcase/package.json +9 -9
- package/continuum-showcase/tests/application.spec.ts +2 -1
- package/full-react-declarative/package.json +10 -10
- package/full-react-declarative/tests/application.spec.ts +2 -1
- package/full-react-imperative/package.json +10 -10
- package/full-react-imperative/tests/application.spec.ts +2 -1
- package/full-service-declarative/package.json +10 -10
- package/full-service-declarative/tests/application.spec.ts +2 -1
- package/full-service-imperative/package.json +10 -10
- package/full-service-imperative/tests/application.spec.ts +2 -1
- package/monorepo-declarative/LICENSE +21 -0
- package/monorepo-declarative/README.md +91 -0
- package/monorepo-declarative/apps/mobile/App.tsx +26 -0
- package/monorepo-declarative/apps/mobile/LICENSE +21 -0
- package/monorepo-declarative/apps/mobile/app/Application.ts +24 -0
- package/monorepo-declarative/apps/mobile/app/PortalMark.tsx +32 -0
- package/monorepo-declarative/apps/mobile/app/TaskListScreen.tsx +67 -0
- package/monorepo-declarative/apps/mobile/app/TaskListView.tsx +98 -0
- package/monorepo-declarative/apps/mobile/app/theme.ts +66 -0
- package/monorepo-declarative/apps/mobile/app.json +32 -0
- package/monorepo-declarative/apps/mobile/assets/android-icon-background.png +0 -0
- package/monorepo-declarative/apps/mobile/assets/android-icon-foreground.png +0 -0
- package/monorepo-declarative/apps/mobile/assets/android-icon-monochrome.png +0 -0
- package/monorepo-declarative/apps/mobile/assets/favicon.png +0 -0
- package/monorepo-declarative/apps/mobile/assets/icon.png +0 -0
- package/monorepo-declarative/apps/mobile/assets/logo-dark.png +0 -0
- package/monorepo-declarative/apps/mobile/assets/logo-light.png +0 -0
- package/monorepo-declarative/apps/mobile/assets/splash-icon.png +0 -0
- package/monorepo-declarative/apps/mobile/babel.config.js +16 -0
- package/monorepo-declarative/apps/mobile/index.ts +30 -0
- package/monorepo-declarative/apps/mobile/metro.config.js +13 -0
- package/monorepo-declarative/apps/mobile/package.json +47 -0
- package/monorepo-declarative/apps/mobile/tests/TaskListScreen.spec.ts +36 -0
- package/monorepo-declarative/apps/mobile/tests/navigation.spec.ts +62 -0
- package/monorepo-declarative/apps/mobile/tests/stubs/react-native.ts +42 -0
- package/monorepo-declarative/apps/mobile/tsconfig.json +15 -0
- package/monorepo-declarative/apps/mobile/vitest.config.ts +41 -0
- package/monorepo-declarative/apps/web/LICENSE +21 -0
- package/monorepo-declarative/apps/web/app/Application.ts +23 -0
- package/monorepo-declarative/apps/web/app/TaskListPage.tsx +95 -0
- package/monorepo-declarative/apps/web/assets/css/index.css +220 -0
- package/monorepo-declarative/apps/web/package.json +41 -0
- package/monorepo-declarative/apps/web/public/favicon.svg +16 -0
- package/monorepo-declarative/apps/web/public/logo.svg +16 -0
- package/monorepo-declarative/apps/web/tests/TaskListPage.spec.ts +38 -0
- package/monorepo-declarative/apps/web/tsconfig.json +33 -0
- package/monorepo-declarative/package.json +28 -0
- package/monorepo-declarative/packages/domain/LICENSE +21 -0
- package/monorepo-declarative/packages/domain/package.json +32 -0
- package/monorepo-declarative/packages/domain/src/Task.ts +16 -0
- package/monorepo-declarative/packages/domain/src/TaskService.ts +75 -0
- package/monorepo-declarative/packages/domain/src/index.ts +33 -0
- package/monorepo-declarative/packages/domain/tests/TaskService.spec.ts +73 -0
- package/monorepo-declarative/packages/domain/tsconfig.build.json +4 -0
- package/monorepo-declarative/packages/domain/tsconfig.json +16 -0
- package/monorepo-declarative/packages/domain/vitest.config.ts +15 -0
- package/monorepo-declarative/pnpm-lock.yaml +7613 -0
- package/monorepo-declarative/pnpm-workspace.yaml +4 -0
- package/package.json +16 -1
- package/standard-react-declarative/package.json +9 -9
- package/standard-react-declarative/tests/application.spec.ts +2 -1
- package/standard-react-imperative/package.json +9 -9
- package/standard-react-imperative/tests/application.spec.ts +2 -1
- package/standard-service-declarative/package.json +7 -7
- package/standard-service-declarative/tests/Application.spec.ts +2 -1
- package/standard-service-imperative/package.json +7 -7
- package/standard-service-imperative/tests/Application.spec.ts +2 -1
|
@@ -20,16 +20,17 @@
|
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"react": "^19.0.0",
|
|
22
22
|
"react-dom": "^19.0.0",
|
|
23
|
-
"@stone-js/core": "^0.8.
|
|
24
|
-
"@stone-js/use-react": "^0.8.
|
|
25
|
-
"@stone-js/browser-adapter": "^0.8.
|
|
23
|
+
"@stone-js/core": "^0.8.15",
|
|
24
|
+
"@stone-js/use-react": "^0.8.15",
|
|
25
|
+
"@stone-js/browser-adapter": "^0.8.15"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@babel/plugin-proposal-decorators": "^7.25.9",
|
|
29
29
|
"@babel/preset-env": "^7.26.9",
|
|
30
30
|
"@babel/preset-typescript": "^7.27.0",
|
|
31
|
-
"@stone-js/
|
|
32
|
-
"@stone-js/
|
|
31
|
+
"@stone-js/browser-core": "^0.8.15",
|
|
32
|
+
"@stone-js/cli": "^0.8.15",
|
|
33
|
+
"@stone-js/testing": "^0.8.15",
|
|
33
34
|
"@types/node": "^24.0.7",
|
|
34
35
|
"@types/react": "^19.0.7",
|
|
35
36
|
"@types/react-dom": "^19.0.3",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { createTestApp
|
|
1
|
+
import { createTestApp } from '@stone-js/testing'
|
|
2
|
+
import { makeIncomingBrowserEvent } from '@stone-js/testing/browser'
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* The whole application, booted in memory and asked a real question.
|
|
@@ -12,7 +13,7 @@ describe('Pages', () => {
|
|
|
12
13
|
it('renders the home page, data included', async () => {
|
|
13
14
|
const app = await createTestApp()
|
|
14
15
|
|
|
15
|
-
const response = await app.send(
|
|
16
|
+
const response = await app.send(makeIncomingBrowserEvent({ url: '/?name=Ada' }))
|
|
16
17
|
|
|
17
18
|
expect(response.statusCode).toBe(200)
|
|
18
19
|
// A page is a handler, so a rendered page is a response whose body is HTML. Query that HTML with
|
|
@@ -23,7 +24,7 @@ describe('Pages', () => {
|
|
|
23
24
|
it('renders without a name', async () => {
|
|
24
25
|
const app = await createTestApp()
|
|
25
26
|
|
|
26
|
-
const response = await app.send(
|
|
27
|
+
const response = await app.send(makeIncomingBrowserEvent({ url: '/' }))
|
|
27
28
|
|
|
28
29
|
expect(response.statusCode).toBe(200)
|
|
29
30
|
})
|
|
@@ -20,16 +20,17 @@
|
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"react": "^19.0.0",
|
|
22
22
|
"react-dom": "^19.0.0",
|
|
23
|
-
"@stone-js/core": "^0.8.
|
|
24
|
-
"@stone-js/use-react": "^0.8.
|
|
25
|
-
"@stone-js/browser-adapter": "^0.8.
|
|
23
|
+
"@stone-js/core": "^0.8.15",
|
|
24
|
+
"@stone-js/use-react": "^0.8.15",
|
|
25
|
+
"@stone-js/browser-adapter": "^0.8.15"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@babel/plugin-proposal-decorators": "^7.25.9",
|
|
29
29
|
"@babel/preset-env": "^7.26.9",
|
|
30
30
|
"@babel/preset-typescript": "^7.27.0",
|
|
31
|
-
"@stone-js/
|
|
32
|
-
"@stone-js/
|
|
31
|
+
"@stone-js/browser-core": "^0.8.15",
|
|
32
|
+
"@stone-js/cli": "^0.8.15",
|
|
33
|
+
"@stone-js/testing": "^0.8.15",
|
|
33
34
|
"@types/node": "^24.0.7",
|
|
34
35
|
"@types/react": "^19.0.7",
|
|
35
36
|
"@types/react-dom": "^19.0.3",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { createTestApp
|
|
1
|
+
import { createTestApp } from '@stone-js/testing'
|
|
2
|
+
import { makeIncomingBrowserEvent } from '@stone-js/testing/browser'
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* The whole application, booted in memory and asked a real question.
|
|
@@ -12,7 +13,7 @@ describe('Pages', () => {
|
|
|
12
13
|
it('renders the home page, data included', async () => {
|
|
13
14
|
const app = await createTestApp()
|
|
14
15
|
|
|
15
|
-
const response = await app.send(
|
|
16
|
+
const response = await app.send(makeIncomingBrowserEvent({ url: '/?name=Ada' }))
|
|
16
17
|
|
|
17
18
|
expect(response.statusCode).toBe(200)
|
|
18
19
|
// A page is a handler, so a rendered page is a response whose body is HTML. Query that HTML with
|
|
@@ -23,7 +24,7 @@ describe('Pages', () => {
|
|
|
23
24
|
it('renders without a name', async () => {
|
|
24
25
|
const app = await createTestApp()
|
|
25
26
|
|
|
26
|
-
const response = await app.send(
|
|
27
|
+
const response = await app.send(makeIncomingBrowserEvent({ url: '/' }))
|
|
27
28
|
|
|
28
29
|
expect(response.statusCode).toBe(200)
|
|
29
30
|
})
|
|
@@ -106,36 +106,29 @@ the page that owns `/`, with `name` readable through `event.get('name')`. Change
|
|
|
106
106
|
|
|
107
107
|
`App.tsx` renders `StoneNativeApp`, which shows the screen on top of the stack. That is the floor,
|
|
108
108
|
not the ceiling: it is what makes the first run work with nothing installed. The platform's
|
|
109
|
-
transitions, the swipe-back gesture, and a screen keeping its own state
|
|
110
|
-
things only a native navigator gives you.
|
|
109
|
+
transitions, the swipe-back gesture, the hardware back button, and a screen keeping its own state
|
|
110
|
+
while another covers it are things only a native navigator gives you.
|
|
111
111
|
|
|
112
|
-
|
|
112
|
+
Two commands and one import away:
|
|
113
113
|
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
114
|
+
```sh
|
|
115
|
+
npx expo install @react-navigation/native @react-navigation/native-stack \
|
|
116
|
+
react-native-screens react-native-safe-area-context
|
|
117
|
+
```
|
|
118
118
|
|
|
119
|
-
|
|
119
|
+
```tsx
|
|
120
|
+
// App.tsx
|
|
121
|
+
import { StoneNativeStack } from '@stone-js/use-react-native/navigation'
|
|
120
122
|
|
|
121
123
|
export default function App () {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
return (
|
|
125
|
-
<NavigationContainer>
|
|
126
|
-
<Stack.Navigator>
|
|
127
|
-
{screens.map((screen) => (
|
|
128
|
-
<Stack.Screen key={screen.key} name={screen.key} options={{ title: screen.title }}>
|
|
129
|
-
{() => screen.element}
|
|
130
|
-
</Stack.Screen>
|
|
131
|
-
))}
|
|
132
|
-
</Stack.Navigator>
|
|
133
|
-
</NavigationContainer>
|
|
134
|
-
)
|
|
124
|
+
return <StoneNativeStack screenOptions={{ headerShown: true }} />
|
|
135
125
|
}
|
|
136
126
|
```
|
|
137
127
|
|
|
138
|
-
Nothing about your
|
|
128
|
+
Nothing about your screens changes. Each becomes a native one, keyed so the navigator keeps its
|
|
129
|
+
state, and titled from the page's `head`. See the
|
|
130
|
+
[renderer's README](https://www.npmjs.com/package/@stone-js/use-react-native) for how the two stacks
|
|
131
|
+
stay in agreement, and for writing your own navigator instead.
|
|
139
132
|
|
|
140
133
|
## The imperative twin
|
|
141
134
|
|
|
@@ -21,11 +21,11 @@
|
|
|
21
21
|
"test:cvg": "npm run test -- --coverage"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@stone-js/browser-core": "^0.8.
|
|
25
|
-
"@stone-js/core": "^0.8.
|
|
26
|
-
"@stone-js/react-native-adapter": "^0.8.
|
|
27
|
-
"@stone-js/router": "^0.8.
|
|
28
|
-
"@stone-js/use-react-native": "^0.8.
|
|
24
|
+
"@stone-js/browser-core": "^0.8.15",
|
|
25
|
+
"@stone-js/core": "^0.8.15",
|
|
26
|
+
"@stone-js/react-native-adapter": "^0.8.15",
|
|
27
|
+
"@stone-js/router": "^0.8.15",
|
|
28
|
+
"@stone-js/use-react-native": "^0.8.15",
|
|
29
29
|
"expo": "~57.0.14",
|
|
30
30
|
"expo-status-bar": "~57.0.1",
|
|
31
31
|
"fast-text-encoding": "^1.0.6",
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@babel/plugin-proposal-decorators": "^7.28.0",
|
|
38
|
-
"@stone-js/cli": "^0.8.
|
|
39
|
-
"@stone-js/testing": "^0.8.
|
|
38
|
+
"@stone-js/cli": "^0.8.15",
|
|
39
|
+
"@stone-js/testing": "^0.8.15",
|
|
40
40
|
"@types/react": "~19.2.2",
|
|
41
41
|
"@vitest/coverage-v8": "^3.0.9",
|
|
42
42
|
"babel-preset-expo": "^57.0.7",
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { createTestApp } from '@stone-js/testing'
|
|
2
|
+
import { makeIncomingBrowserEvent } from '@stone-js/testing/browser'
|
|
3
|
+
import { REACT_NATIVE_PLATFORM } from '@stone-js/react-native-adapter'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The whole application, booted in memory and asked a real question.
|
|
7
|
+
*
|
|
8
|
+
* Nothing is mocked and nothing is listed: `createTestApp()` discovers `app/**`, and the event goes
|
|
9
|
+
* through the same kernel a phone runs. This is the same test the web starters ship, with one
|
|
10
|
+
* difference: the platform is named, and the event is the one a native application receives, which
|
|
11
|
+
* is how a deep link arrives.
|
|
12
|
+
*
|
|
13
|
+
* What it substitutes is the adapter, and that is the architecture rather than a concession to
|
|
14
|
+
* testing: the domain is written once and the context is chosen at run time, so under Node the
|
|
15
|
+
* context is a test adapter instead of React Native.
|
|
16
|
+
*/
|
|
17
|
+
describe('HomeScreen', () => {
|
|
18
|
+
it('resolves the home screen, data included', async () => {
|
|
19
|
+
const app = await createTestApp({ platform: REACT_NATIVE_PLATFORM })
|
|
20
|
+
|
|
21
|
+
const response = await app.send(makeIncomingBrowserEvent({ url: 'stone://app/?name=Ada' }))
|
|
22
|
+
|
|
23
|
+
expect(response.statusCode).toBe(200)
|
|
24
|
+
// The head is what a navigator shows in its header, and it proves the loader ran with the deep
|
|
25
|
+
// link's parameter.
|
|
26
|
+
expect((response.content as any).head.title).toBe('Ada · Welcome to Stone.js')
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('resolves without a name', async () => {
|
|
30
|
+
const app = await createTestApp({ platform: REACT_NATIVE_PLATFORM })
|
|
31
|
+
|
|
32
|
+
const response = await app.send(makeIncomingBrowserEvent({ url: 'stone://app/' }))
|
|
33
|
+
|
|
34
|
+
expect(response.statusCode).toBe(200)
|
|
35
|
+
})
|
|
36
|
+
})
|
|
@@ -5,18 +5,21 @@ import { ScreenStack } from '@stone-js/use-react-native'
|
|
|
5
5
|
import { NavigationSource } from '@stone-js/react-native-adapter'
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* Navigation, with the real adapter this time.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
10
|
+
* `HomeScreen.spec.ts` asks what a route resolves to, which is the same question on every platform
|
|
11
|
+
* and needs no adapter. This asks the native question instead: what lands on the navigation stack,
|
|
12
|
+
* and in what order. So nothing is substituted here. The kernel, the router, the React Native adapter
|
|
13
|
+
* and the renderer are the ones a phone runs; what a device adds is a screen to draw on.
|
|
14
|
+
*
|
|
15
|
+
* Two things are supplied rather than discovered, both through documented configuration: the
|
|
16
|
+
* navigation source, so the test can send a deep link, and the screen stack, so it can read what
|
|
17
|
+
* landed on it.
|
|
15
18
|
*
|
|
16
19
|
* Screens are listed here because the generated manifest (`.stone/modules.ts`) is Metro's, written
|
|
17
20
|
* when Metro starts. Under a test runner the modules are imported directly, which is the same set.
|
|
18
21
|
*/
|
|
19
|
-
describe('
|
|
22
|
+
describe('Navigation', () => {
|
|
20
23
|
const screens = ScreenStack.create()
|
|
21
24
|
const navigation = NavigationSource.create({ baseUrl: 'stone://app' })
|
|
22
25
|
|
|
@@ -22,6 +22,12 @@ export default defineConfig({
|
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
24
|
test: {
|
|
25
|
+
// `createTestApp()` discovers the application's modules by importing them at run time. Vitest
|
|
26
|
+
// externalises dependencies from `node_modules`, and an externalised module's `import()` is
|
|
27
|
+
// Node's, which cannot load a `.ts` file. Inlining the package puts those imports back through
|
|
28
|
+
// Vite's transform. `stone test` does this for you; a project running Vitest directly, as an
|
|
29
|
+
// Expo project does, states it here.
|
|
30
|
+
server: { deps: { inline: ['@stone-js/testing'] } },
|
|
25
31
|
globals: true,
|
|
26
32
|
environment: 'node',
|
|
27
33
|
include: ['./tests/**/*.{test,spec}.?(c|m)[jt]s?(x)'],
|
|
@@ -106,36 +106,29 @@ the page that owns `/`, with `name` readable through `event.get('name')`. Change
|
|
|
106
106
|
|
|
107
107
|
`App.tsx` renders `StoneNativeApp`, which shows the screen on top of the stack. That is the floor,
|
|
108
108
|
not the ceiling: it is what makes the first run work with nothing installed. The platform's
|
|
109
|
-
transitions, the swipe-back gesture, and a screen keeping its own state
|
|
110
|
-
things only a native navigator gives you.
|
|
109
|
+
transitions, the swipe-back gesture, the hardware back button, and a screen keeping its own state
|
|
110
|
+
while another covers it are things only a native navigator gives you.
|
|
111
111
|
|
|
112
|
-
|
|
112
|
+
Two commands and one import away:
|
|
113
113
|
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
114
|
+
```sh
|
|
115
|
+
npx expo install @react-navigation/native @react-navigation/native-stack \
|
|
116
|
+
react-native-screens react-native-safe-area-context
|
|
117
|
+
```
|
|
118
118
|
|
|
119
|
-
|
|
119
|
+
```tsx
|
|
120
|
+
// App.tsx
|
|
121
|
+
import { StoneNativeStack } from '@stone-js/use-react-native/navigation'
|
|
120
122
|
|
|
121
123
|
export default function App () {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
return (
|
|
125
|
-
<NavigationContainer>
|
|
126
|
-
<Stack.Navigator>
|
|
127
|
-
{screens.map((screen) => (
|
|
128
|
-
<Stack.Screen key={screen.key} name={screen.key} options={{ title: screen.title }}>
|
|
129
|
-
{() => screen.element}
|
|
130
|
-
</Stack.Screen>
|
|
131
|
-
))}
|
|
132
|
-
</Stack.Navigator>
|
|
133
|
-
</NavigationContainer>
|
|
134
|
-
)
|
|
124
|
+
return <StoneNativeStack screenOptions={{ headerShown: true }} />
|
|
135
125
|
}
|
|
136
126
|
```
|
|
137
127
|
|
|
138
|
-
Nothing about your
|
|
128
|
+
Nothing about your screens changes. Each becomes a native one, keyed so the navigator keeps its
|
|
129
|
+
state, and titled from the page's `head`. See the
|
|
130
|
+
[renderer's README](https://www.npmjs.com/package/@stone-js/use-react-native) for how the two stacks
|
|
131
|
+
stay in agreement, and for writing your own navigator instead.
|
|
139
132
|
|
|
140
133
|
## The declarative twin
|
|
141
134
|
|
|
@@ -21,11 +21,11 @@
|
|
|
21
21
|
"test:cvg": "npm run test -- --coverage"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@stone-js/browser-core": "^0.8.
|
|
25
|
-
"@stone-js/core": "^0.8.
|
|
26
|
-
"@stone-js/react-native-adapter": "^0.8.
|
|
27
|
-
"@stone-js/router": "^0.8.
|
|
28
|
-
"@stone-js/use-react-native": "^0.8.
|
|
24
|
+
"@stone-js/browser-core": "^0.8.15",
|
|
25
|
+
"@stone-js/core": "^0.8.15",
|
|
26
|
+
"@stone-js/react-native-adapter": "^0.8.15",
|
|
27
|
+
"@stone-js/router": "^0.8.15",
|
|
28
|
+
"@stone-js/use-react-native": "^0.8.15",
|
|
29
29
|
"expo": "~57.0.14",
|
|
30
30
|
"expo-status-bar": "~57.0.1",
|
|
31
31
|
"fast-text-encoding": "^1.0.6",
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@babel/plugin-proposal-decorators": "^7.28.0",
|
|
38
|
-
"@stone-js/cli": "^0.8.
|
|
39
|
-
"@stone-js/testing": "^0.8.
|
|
38
|
+
"@stone-js/cli": "^0.8.15",
|
|
39
|
+
"@stone-js/testing": "^0.8.15",
|
|
40
40
|
"@types/react": "~19.2.2",
|
|
41
41
|
"@vitest/coverage-v8": "^3.0.9",
|
|
42
42
|
"babel-preset-expo": "^57.0.7",
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { createTestApp } from '@stone-js/testing'
|
|
2
|
+
import { makeIncomingBrowserEvent } from '@stone-js/testing/browser'
|
|
3
|
+
import { REACT_NATIVE_PLATFORM } from '@stone-js/react-native-adapter'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The whole application, booted in memory and asked a real question.
|
|
7
|
+
*
|
|
8
|
+
* Nothing is mocked and nothing is listed: `createTestApp()` discovers `app/**`, and the event goes
|
|
9
|
+
* through the same kernel a phone runs. This is the same test the web starters ship, with one
|
|
10
|
+
* difference: the platform is named, and the event is the one a native application receives, which
|
|
11
|
+
* is how a deep link arrives.
|
|
12
|
+
*
|
|
13
|
+
* What it substitutes is the adapter, and that is the architecture rather than a concession to
|
|
14
|
+
* testing: the domain is written once and the context is chosen at run time, so under Node the
|
|
15
|
+
* context is a test adapter instead of React Native.
|
|
16
|
+
*/
|
|
17
|
+
describe('HomeScreen', () => {
|
|
18
|
+
it('resolves the home screen, data included', async () => {
|
|
19
|
+
const app = await createTestApp({ platform: REACT_NATIVE_PLATFORM })
|
|
20
|
+
|
|
21
|
+
const response = await app.send(makeIncomingBrowserEvent({ url: 'stone://app/?name=Ada' }))
|
|
22
|
+
|
|
23
|
+
expect(response.statusCode).toBe(200)
|
|
24
|
+
// The head is what a navigator shows in its header, and it proves the loader ran with the deep
|
|
25
|
+
// link's parameter.
|
|
26
|
+
expect((response.content as any).head.title).toBe('Ada · Welcome to Stone.js')
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('resolves without a name', async () => {
|
|
30
|
+
const app = await createTestApp({ platform: REACT_NATIVE_PLATFORM })
|
|
31
|
+
|
|
32
|
+
const response = await app.send(makeIncomingBrowserEvent({ url: 'stone://app/' }))
|
|
33
|
+
|
|
34
|
+
expect(response.statusCode).toBe(200)
|
|
35
|
+
})
|
|
36
|
+
})
|
|
@@ -5,18 +5,21 @@ import { ScreenStack } from '@stone-js/use-react-native'
|
|
|
5
5
|
import { NavigationSource } from '@stone-js/react-native-adapter'
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* Navigation, with the real adapter this time.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
10
|
+
* `HomeScreen.spec.ts` asks what a route resolves to, which is the same question on every platform
|
|
11
|
+
* and needs no adapter. This asks the native question instead: what lands on the navigation stack,
|
|
12
|
+
* and in what order. So nothing is substituted here. The kernel, the router, the React Native adapter
|
|
13
|
+
* and the renderer are the ones a phone runs; what a device adds is a screen to draw on.
|
|
14
|
+
*
|
|
15
|
+
* Two things are supplied rather than discovered, both through documented configuration: the
|
|
16
|
+
* navigation source, so the test can send a deep link, and the screen stack, so it can read what
|
|
17
|
+
* landed on it.
|
|
15
18
|
*
|
|
16
19
|
* Screens are listed here because the generated manifest (`.stone/modules.ts`) is Metro's, written
|
|
17
20
|
* when Metro starts. Under a test runner the modules are imported directly, which is the same set.
|
|
18
21
|
*/
|
|
19
|
-
describe('
|
|
22
|
+
describe('Navigation', () => {
|
|
20
23
|
const screens = ScreenStack.create()
|
|
21
24
|
const navigation = NavigationSource.create({ baseUrl: 'stone://app' })
|
|
22
25
|
|
|
@@ -22,6 +22,12 @@ export default defineConfig({
|
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
24
|
test: {
|
|
25
|
+
// `createTestApp()` discovers the application's modules by importing them at run time. Vitest
|
|
26
|
+
// externalises dependencies from `node_modules`, and an externalised module's `import()` is
|
|
27
|
+
// Node's, which cannot load a `.ts` file. Inlining the package puts those imports back through
|
|
28
|
+
// Vite's transform. `stone test` does this for you; a project running Vitest directly, as an
|
|
29
|
+
// Expo project does, states it here.
|
|
30
|
+
server: { deps: { inline: ['@stone-js/testing'] } },
|
|
25
31
|
globals: true,
|
|
26
32
|
environment: 'node',
|
|
27
33
|
include: ['./tests/**/*.{test,spec}.?(c|m)[jt]s?(x)'],
|
|
@@ -18,15 +18,16 @@
|
|
|
18
18
|
"test:cvg": "stone test --coverage"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@stone-js/core": "^0.8.
|
|
22
|
-
"@stone-js/node-http-adapter": "^0.8.
|
|
21
|
+
"@stone-js/core": "^0.8.15",
|
|
22
|
+
"@stone-js/node-http-adapter": "^0.8.15"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@babel/plugin-proposal-decorators": "^7.25.9",
|
|
26
26
|
"@babel/preset-env": "^7.26.9",
|
|
27
27
|
"@babel/preset-typescript": "^7.27.0",
|
|
28
|
-
"@stone-js/cli": "^0.8.
|
|
29
|
-
"@stone-js/
|
|
28
|
+
"@stone-js/cli": "^0.8.15",
|
|
29
|
+
"@stone-js/http-core": "^0.8.15",
|
|
30
|
+
"@stone-js/testing": "^0.8.15",
|
|
30
31
|
"@types/node": "^24.0.7",
|
|
31
32
|
"@vitest/coverage-v8": "^3.0.9",
|
|
32
33
|
"vitest": "^3.0.9"
|
|
@@ -18,15 +18,16 @@
|
|
|
18
18
|
"test:cvg": "stone test --coverage"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@stone-js/core": "^0.8.
|
|
22
|
-
"@stone-js/node-http-adapter": "^0.8.
|
|
21
|
+
"@stone-js/core": "^0.8.15",
|
|
22
|
+
"@stone-js/node-http-adapter": "^0.8.15"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@babel/plugin-proposal-decorators": "^7.25.9",
|
|
26
26
|
"@babel/preset-env": "^7.26.9",
|
|
27
27
|
"@babel/preset-typescript": "^7.27.0",
|
|
28
|
-
"@stone-js/cli": "^0.8.
|
|
29
|
-
"@stone-js/
|
|
28
|
+
"@stone-js/cli": "^0.8.15",
|
|
29
|
+
"@stone-js/http-core": "^0.8.15",
|
|
30
|
+
"@stone-js/testing": "^0.8.15",
|
|
30
31
|
"@types/node": "^24.0.7",
|
|
31
32
|
"@vitest/coverage-v8": "^3.0.9",
|
|
32
33
|
"vitest": "^3.0.9"
|
|
@@ -15,20 +15,20 @@
|
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"react": "^19.0.0",
|
|
17
17
|
"react-dom": "^19.0.0",
|
|
18
|
-
"@stone-js/core": "^0.8.
|
|
19
|
-
"@stone-js/router": "^0.8.
|
|
20
|
-
"@stone-js/use-react": "^0.8.
|
|
21
|
-
"@stone-js/http-core": "^0.8.
|
|
22
|
-
"@stone-js/pipeline": "^0.8.
|
|
23
|
-
"@stone-js/browser-adapter": "^0.8.
|
|
24
|
-
"@stone-js/node-http-adapter": "^0.8.
|
|
18
|
+
"@stone-js/core": "^0.8.15",
|
|
19
|
+
"@stone-js/router": "^0.8.15",
|
|
20
|
+
"@stone-js/use-react": "^0.8.15",
|
|
21
|
+
"@stone-js/http-core": "^0.8.15",
|
|
22
|
+
"@stone-js/pipeline": "^0.8.15",
|
|
23
|
+
"@stone-js/browser-adapter": "^0.8.15",
|
|
24
|
+
"@stone-js/node-http-adapter": "^0.8.15"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@babel/plugin-proposal-decorators": "^7.25.9",
|
|
28
28
|
"@babel/preset-env": "^7.26.9",
|
|
29
29
|
"@babel/preset-typescript": "^7.27.0",
|
|
30
|
-
"@stone-js/cli": "^0.8.
|
|
31
|
-
"@stone-js/testing": "^0.8.
|
|
30
|
+
"@stone-js/cli": "^0.8.15",
|
|
31
|
+
"@stone-js/testing": "^0.8.15",
|
|
32
32
|
"@types/node": "^24.0.7",
|
|
33
33
|
"@types/react": "^19.0.7",
|
|
34
34
|
"@types/react-dom": "^19.0.3",
|
|
@@ -18,14 +18,14 @@
|
|
|
18
18
|
"test:cvg": "stone test --coverage"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@stone-js/env": "^0.8.
|
|
22
|
-
"@stone-js/http-core": "^0.8.
|
|
23
|
-
"@stone-js/node-http-adapter": "^0.8.
|
|
24
|
-
"@stone-js/router": "^0.8.
|
|
25
|
-
"@stone-js/filesystem": "^0.8.
|
|
26
|
-
"@stone-js/core": "^0.8.
|
|
27
|
-
"@stone-js/use-react": "^0.8.
|
|
28
|
-
"@stone-js/browser-adapter": "^0.8.
|
|
21
|
+
"@stone-js/env": "^0.8.15",
|
|
22
|
+
"@stone-js/http-core": "^0.8.15",
|
|
23
|
+
"@stone-js/node-http-adapter": "^0.8.15",
|
|
24
|
+
"@stone-js/router": "^0.8.15",
|
|
25
|
+
"@stone-js/filesystem": "^0.8.15",
|
|
26
|
+
"@stone-js/core": "^0.8.15",
|
|
27
|
+
"@stone-js/use-react": "^0.8.15",
|
|
28
|
+
"@stone-js/browser-adapter": "^0.8.15",
|
|
29
29
|
"axios": "^1.7.9",
|
|
30
30
|
"react": "^19.0.0",
|
|
31
31
|
"react-dom": "^19.0.0"
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"@babel/plugin-proposal-decorators": "^7.25.9",
|
|
35
35
|
"@babel/preset-env": "^7.26.9",
|
|
36
36
|
"@babel/preset-typescript": "^7.27.0",
|
|
37
|
-
"@stone-js/cli": "^0.8.
|
|
38
|
-
"@stone-js/testing": "^0.8.
|
|
37
|
+
"@stone-js/cli": "^0.8.15",
|
|
38
|
+
"@stone-js/testing": "^0.8.15",
|
|
39
39
|
"@types/axios": "^0.14.4",
|
|
40
40
|
"@types/node": "^24.0.7",
|
|
41
41
|
"@types/react": "^19.0.7",
|
|
@@ -18,14 +18,14 @@
|
|
|
18
18
|
"test:cvg": "stone test --coverage"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@stone-js/env": "^0.8.
|
|
22
|
-
"@stone-js/http-core": "^0.8.
|
|
23
|
-
"@stone-js/node-http-adapter": "^0.8.
|
|
24
|
-
"@stone-js/router": "^0.8.
|
|
25
|
-
"@stone-js/filesystem": "^0.8.
|
|
26
|
-
"@stone-js/core": "^0.8.
|
|
27
|
-
"@stone-js/use-react": "^0.8.
|
|
28
|
-
"@stone-js/browser-adapter": "^0.8.
|
|
21
|
+
"@stone-js/env": "^0.8.15",
|
|
22
|
+
"@stone-js/http-core": "^0.8.15",
|
|
23
|
+
"@stone-js/node-http-adapter": "^0.8.15",
|
|
24
|
+
"@stone-js/router": "^0.8.15",
|
|
25
|
+
"@stone-js/filesystem": "^0.8.15",
|
|
26
|
+
"@stone-js/core": "^0.8.15",
|
|
27
|
+
"@stone-js/use-react": "^0.8.15",
|
|
28
|
+
"@stone-js/browser-adapter": "^0.8.15",
|
|
29
29
|
"axios": "^1.7.9",
|
|
30
30
|
"dayjs": "^1.11.13",
|
|
31
31
|
"react": "^19.0.0",
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
"@babel/plugin-proposal-decorators": "^7.25.9",
|
|
36
36
|
"@babel/preset-env": "^7.26.9",
|
|
37
37
|
"@babel/preset-typescript": "^7.27.0",
|
|
38
|
-
"@stone-js/cli": "^0.8.
|
|
39
|
-
"@stone-js/testing": "^0.8.
|
|
38
|
+
"@stone-js/cli": "^0.8.15",
|
|
39
|
+
"@stone-js/testing": "^0.8.15",
|
|
40
40
|
"@types/axios": "^0.14.4",
|
|
41
41
|
"@types/node": "^24.0.7",
|
|
42
42
|
"@types/react": "^19.0.7",
|