@stone-js/starters 0.8.7 → 0.8.9
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/README.md +2 -2
- package/basic-react-declarative/app/Application.tsx +0 -2
- package/basic-react-declarative/package.json +7 -7
- package/basic-react-declarative/public/favicon.svg +16 -0
- package/basic-react-declarative/tests/Application.spec.ts +25 -100
- package/basic-react-imperative/app/Application.tsx +2 -17
- package/basic-react-imperative/package.json +7 -7
- package/basic-react-imperative/public/favicon.svg +16 -0
- package/basic-react-imperative/tests/Application.spec.ts +25 -86
- package/basic-service-declarative/app/Application.ts +0 -2
- package/basic-service-declarative/package.json +6 -6
- package/basic-service-declarative/tests/Application.spec.ts +20 -53
- package/basic-service-imperative/app/Application.ts +2 -14
- package/basic-service-imperative/package.json +6 -6
- package/basic-service-imperative/tests/Application.spec.ts +19 -44
- package/continuum-showcase/package.json +11 -10
- package/continuum-showcase/public/favicon.svg +16 -0
- package/continuum-showcase/stone.config.mjs +2 -2
- package/continuum-showcase/tests/application.spec.ts +30 -0
- package/full-react-declarative/app/Application.ts +0 -4
- package/full-react-declarative/package.json +12 -12
- package/full-react-declarative/public/favicon.svg +16 -0
- package/full-react-declarative/stone.config.mjs +2 -2
- package/full-react-declarative/tests/application.spec.ts +30 -0
- package/full-react-imperative/app/Application.ts +1 -3
- package/full-react-imperative/package.json +12 -12
- package/full-react-imperative/public/favicon.svg +16 -0
- package/full-react-imperative/stone.config.mjs +2 -2
- package/full-react-imperative/tests/application.spec.ts +30 -0
- package/full-service-declarative/app/Application.ts +5 -2
- package/full-service-declarative/app/configurations/AppConfiguration.ts +1 -3
- package/full-service-declarative/package.json +13 -12
- package/full-service-declarative/stone.config.mjs +2 -2
- package/full-service-declarative/tests/application.spec.ts +30 -0
- package/full-service-imperative/app/Application.ts +8 -2
- package/full-service-imperative/app/configurations/AppConfiguration.ts +1 -3
- package/full-service-imperative/package.json +13 -12
- package/full-service-imperative/stone.config.mjs +2 -2
- package/full-service-imperative/tests/application.spec.ts +30 -0
- package/package.json +1 -1
- package/standard-react-declarative/package.json +11 -10
- package/standard-react-declarative/public/favicon.svg +16 -0
- package/standard-react-declarative/tests/application.spec.ts +30 -0
- package/standard-react-imperative/package.json +11 -10
- package/standard-react-imperative/public/favicon.svg +16 -0
- package/standard-react-imperative/tests/application.spec.ts +30 -0
- package/standard-service-declarative/package.json +9 -8
- package/standard-service-declarative/tests/Application.spec.ts +22 -94
- package/standard-service-imperative/package.json +9 -8
- package/standard-service-imperative/tests/Application.spec.ts +22 -78
- package/basic-react-declarative/vitest.config.ts +0 -24
- package/basic-react-imperative/vitest.config.ts +0 -24
- package/basic-service-declarative/vitest.config.ts +0 -24
- package/basic-service-imperative/vitest.config.ts +0 -24
- package/continuum-showcase/tests/HomePage.spec.tsx +0 -41
- package/continuum-showcase/vitest.config.ts +0 -31
- package/full-react-declarative/tests/AppLayout.spec.ts +0 -51
- package/full-react-declarative/tests/HomePage.spec.ts +0 -38
- package/full-react-declarative/vitest.config.ts +0 -24
- package/full-react-imperative/tests/HomePage.spec.ts +0 -45
- package/full-react-imperative/tests/LayoutHeader.spec.ts +0 -39
- package/full-react-imperative/vitest.config.ts +0 -24
- package/full-service-declarative/tests/PostService.spec.ts +0 -56
- package/full-service-declarative/tests/WelcomeEventHandler.spec.ts +0 -54
- package/full-service-declarative/vitest.config.ts +0 -24
- package/full-service-imperative/tests/PostService.spec.ts +0 -47
- package/full-service-imperative/tests/WelcomeEventHandler.spec.ts +0 -31
- package/full-service-imperative/vitest.config.ts +0 -24
- package/standard-react-declarative/tests/WelcomePage.spec.ts +0 -71
- package/standard-react-declarative/vitest.config.ts +0 -24
- package/standard-react-imperative/tests/WelcomePage.spec.ts +0 -53
- package/standard-react-imperative/vitest.config.ts +0 -24
- package/standard-service-declarative/vitest.config.ts +0 -24
- package/standard-service-imperative/vitest.config.ts +0 -24
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://opensource.org/licenses/MIT)
|
|
4
4
|
[](https://stonejs.dev)
|
|
5
|
-
[](https://github.com/stone-foundation/stone-js-
|
|
5
|
+
[](https://github.com/stone-foundation/stone-js-framework)
|
|
6
6
|
|
|
7
7
|
This repository contains official **starter templates** for Stone.js applications.
|
|
8
8
|
|
|
@@ -98,4 +98,4 @@ Explore the full documentation: [https://stonejs.dev](https://stonejs.dev)
|
|
|
98
98
|
|
|
99
99
|
## Contributing
|
|
100
100
|
|
|
101
|
-
See [Contributing Guide](https://github.com/stone-foundation/stone-js-
|
|
101
|
+
See [Contributing Guide](https://github.com/stone-foundation/stone-js-framework/blob/main/CONTRIBUTING.md)
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { JSX } from 'react'
|
|
2
2
|
import { Browser } from '@stone-js/browser-adapter'
|
|
3
|
-
import { NodeConsole } from '@stone-js/node-cli-adapter'
|
|
4
3
|
import { ILogger, LogLevel, Promiseable, StoneApp } from '@stone-js/core'
|
|
5
4
|
import { ReactIncomingEvent, UseReact, HeadContext, IPage, PageHeadContext, PageRenderContext } from '@stone-js/use-react'
|
|
6
5
|
|
|
@@ -15,7 +14,6 @@ import { ReactIncomingEvent, UseReact, HeadContext, IPage, PageHeadContext, Page
|
|
|
15
14
|
*/
|
|
16
15
|
@Browser()
|
|
17
16
|
@UseReact()
|
|
18
|
-
@NodeConsole()
|
|
19
17
|
@StoneApp({ logger: { level: LogLevel.INFO } })
|
|
20
18
|
export class Application implements IPage<ReactIncomingEvent> {
|
|
21
19
|
/**
|
|
@@ -14,22 +14,22 @@
|
|
|
14
14
|
"build": "stone build",
|
|
15
15
|
"preview": "stone preview",
|
|
16
16
|
"build:ssr": "stone build --ssr",
|
|
17
|
-
"test": "
|
|
18
|
-
"test:cvg": "
|
|
17
|
+
"test": "stone test",
|
|
18
|
+
"test:cvg": "stone test --coverage"
|
|
19
19
|
},
|
|
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.
|
|
26
|
-
"@stone-js/node-cli-adapter": "^0.8.7"
|
|
23
|
+
"@stone-js/core": "^0.8.9",
|
|
24
|
+
"@stone-js/use-react": "^0.8.9",
|
|
25
|
+
"@stone-js/browser-adapter": "^0.8.9"
|
|
27
26
|
},
|
|
28
27
|
"devDependencies": {
|
|
29
28
|
"@babel/plugin-proposal-decorators": "^7.25.9",
|
|
30
29
|
"@babel/preset-env": "^7.26.9",
|
|
31
30
|
"@babel/preset-typescript": "^7.27.0",
|
|
32
|
-
"@stone-js/cli": "^0.8.
|
|
31
|
+
"@stone-js/cli": "^0.8.9",
|
|
32
|
+
"@stone-js/testing": "^0.8.9",
|
|
33
33
|
"@types/node": "^24.0.7",
|
|
34
34
|
"@types/react": "^19.0.7",
|
|
35
35
|
"@types/react-dom": "^19.0.3",
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96 96" width="96" height="96" role="img" aria-label="Stone.js">
|
|
2
|
+
<title>Stone.js</title>
|
|
3
|
+
<style>
|
|
4
|
+
.ink { stroke: #1B1613; }
|
|
5
|
+
@media (prefers-color-scheme: dark) { .ink { stroke: #EFE8DC; } }
|
|
6
|
+
</style>
|
|
7
|
+
<defs>
|
|
8
|
+
<linearGradient id="braise" gradientUnits="userSpaceOnUse" x1="22" y1="14" x2="74" y2="26.1">
|
|
9
|
+
<stop offset="0" stop-color="#FFC46B" />
|
|
10
|
+
<stop offset="1" stop-color="#FF5A1F" />
|
|
11
|
+
</linearGradient>
|
|
12
|
+
</defs>
|
|
13
|
+
<path class="ink" d="M 79.96 36.4 A 34 34 0 0 1 53.9 81.5" fill="none" stroke-width="11" stroke-linecap="round" opacity=".85" />
|
|
14
|
+
<path class="ink" d="M 42.1 81.5 A 34 34 0 0 1 16.05 36.4" fill="none" stroke-width="11" stroke-linecap="round" opacity=".85" />
|
|
15
|
+
<path d="M 22 26.1 A 34 34 0 0 1 74 26.1" fill="none" stroke="url(#braise)" stroke-width="11" stroke-linecap="round" />
|
|
16
|
+
</svg>
|
|
@@ -1,105 +1,30 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
// We must mock decorators to lighten the test environment
|
|
25
|
-
vi.mock('@stone-js/browser-adapter', async (importOriginal) => {
|
|
26
|
-
const actual: any = await importOriginal()
|
|
27
|
-
return {
|
|
28
|
-
...actual,
|
|
29
|
-
Browser: vi.fn(() => vi.fn()),
|
|
30
|
-
}
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
// We must mock decorators to lighten the test environment
|
|
34
|
-
vi.mock('@stone-js/use-react', async (importOriginal) => {
|
|
35
|
-
const actual: any = await importOriginal()
|
|
36
|
-
return {
|
|
37
|
-
...actual,
|
|
38
|
-
UseReact: vi.fn(() => vi.fn()),
|
|
39
|
-
}
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
describe('Application', () => {
|
|
43
|
-
let app: Application
|
|
44
|
-
let mockedLogger: ILogger
|
|
45
|
-
|
|
46
|
-
beforeEach(() => {
|
|
47
|
-
mockedLogger = {
|
|
48
|
-
info: vi.fn(),
|
|
49
|
-
} as unknown as ILogger
|
|
50
|
-
|
|
51
|
-
app = new Application({ logger: mockedLogger })
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
it('should create an application instance', () => {
|
|
55
|
-
// Assert
|
|
56
|
-
expect(app).toBeInstanceOf(Application)
|
|
1
|
+
import { createTestApp, makeIncomingHttpEvent } from '@stone-js/testing'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The whole application, booted in memory and asked a real question.
|
|
5
|
+
*
|
|
6
|
+
* Nothing is mocked and nothing is listed: `createTestApp()` discovers `app/**`, and the event goes
|
|
7
|
+
* through the same kernel production runs. If this passes the application works; if it fails the
|
|
8
|
+
* application is broken. That is the only kind of test worth shipping in a starter — the previous one
|
|
9
|
+
* stubbed out the framework's own decorators, which meant it could pass while nothing worked.
|
|
10
|
+
*/
|
|
11
|
+
describe('Pages', () => {
|
|
12
|
+
it('renders the home page, data included', async () => {
|
|
13
|
+
const app = await createTestApp()
|
|
14
|
+
|
|
15
|
+
const response = await app.send(makeIncomingHttpEvent({ method: 'GET', url: '/?name=Ada' }))
|
|
16
|
+
|
|
17
|
+
expect(response.statusCode).toBe(200)
|
|
18
|
+
// A page is a handler, so a rendered page is a response whose body is HTML. Query that HTML with
|
|
19
|
+
// whatever you already use (happy-dom, jsdom, Testing Library): none is bundled here.
|
|
20
|
+
expect(response.html()).toContain('Ada')
|
|
57
21
|
})
|
|
58
22
|
|
|
59
|
-
it('
|
|
60
|
-
|
|
61
|
-
const event = { get: () => 'World' } as unknown as ReactIncomingEvent
|
|
23
|
+
it('renders without a name', async () => {
|
|
24
|
+
const app = await createTestApp()
|
|
62
25
|
|
|
63
|
-
|
|
64
|
-
const head = await app.head({ event })
|
|
26
|
+
const response = await app.send(makeIncomingHttpEvent({ method: 'GET', url: '/' }))
|
|
65
27
|
|
|
66
|
-
|
|
67
|
-
expect(head).toHaveProperty('metas')
|
|
68
|
-
expect(head.title).toContain('Welcome to Stone.js')
|
|
69
|
-
expect(head.title).toContain('World')
|
|
70
|
-
expect(head).toHaveProperty('description')
|
|
28
|
+
expect(response.statusCode).toBe(200)
|
|
71
29
|
})
|
|
72
|
-
|
|
73
|
-
it('should handle incoming events', () => {
|
|
74
|
-
// Arrange
|
|
75
|
-
const expectedMessage = 'Hello World!'
|
|
76
|
-
const event = { get: () => 'World' } as unknown as ReactIncomingEvent
|
|
77
|
-
|
|
78
|
-
// Act
|
|
79
|
-
const response = app.handle(event) as { message: string }
|
|
80
|
-
|
|
81
|
-
// Assert
|
|
82
|
-
expect(response.message).toBe(expectedMessage)
|
|
83
|
-
expect(mockedLogger.info).toHaveBeenCalledWith(expectedMessage)
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
it('should render the message', () => {
|
|
87
|
-
// Arrange
|
|
88
|
-
const message = 'Hello World!'
|
|
89
|
-
|
|
90
|
-
// Act
|
|
91
|
-
const response = renderToString(app.render({ data: { message } } as any))
|
|
92
|
-
|
|
93
|
-
// Assert
|
|
94
|
-
expect(response).toContain(message)
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
it('should render the Stone.js logo', () => {
|
|
98
|
-
// Act
|
|
99
|
-
const response = renderToString(app.render({ data: { message: 'Hello World!' } } as any))
|
|
100
|
-
|
|
101
|
-
// Assert
|
|
102
|
-
expect(response).toContain('/logo.svg')
|
|
103
|
-
expect(response).toContain('alt="Stone.js"')
|
|
104
|
-
})
|
|
105
|
-
})
|
|
30
|
+
})
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { JSX } from 'react'
|
|
2
2
|
import { browserAdapterBlueprint } from '@stone-js/browser-adapter'
|
|
3
|
-
import {
|
|
4
|
-
import { defineCommand, FactoryCommandHandler, NODE_CONSOLE_PLATFORM, nodeConsoleAdapterBlueprint } from '@stone-js/node-cli-adapter'
|
|
3
|
+
import { ILogger, Promiseable } from '@stone-js/core'
|
|
5
4
|
import { defineStoneReactApp, HeadContext, IPage, PageHeadContext, PageRenderContext, ReactIncomingEvent } from '@stone-js/use-react'
|
|
6
5
|
|
|
7
6
|
/**
|
|
@@ -73,23 +72,9 @@ export const FactoryHandler = ({ logger }: AppOptions): IPage<ReactIncomingEvent
|
|
|
73
72
|
export const Application = defineStoneReactApp(
|
|
74
73
|
FactoryHandler,
|
|
75
74
|
{ debug: true, isFactory: true },
|
|
76
|
-
[browserAdapterBlueprint
|
|
75
|
+
[browserAdapterBlueprint]
|
|
77
76
|
)
|
|
78
77
|
|
|
79
|
-
/**
|
|
80
|
-
* Application configuration.
|
|
81
|
-
*/
|
|
82
|
-
export const AppConfig = defineConfig({
|
|
83
|
-
afterConfigure (blueprint: IBlueprint) {
|
|
84
|
-
if (
|
|
85
|
-
isNotEmpty<FactoryCommandHandler>(FactoryHandler) &&
|
|
86
|
-
blueprint.is('stone.adapter.platform', NODE_CONSOLE_PLATFORM)
|
|
87
|
-
) {
|
|
88
|
-
blueprint.set(defineCommand(FactoryHandler, { name: '*', isFactory: true }))
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
})
|
|
92
|
-
|
|
93
78
|
/**
|
|
94
79
|
* Application options.
|
|
95
80
|
*/
|
|
@@ -14,22 +14,22 @@
|
|
|
14
14
|
"build": "stone build",
|
|
15
15
|
"preview": "stone preview",
|
|
16
16
|
"typings": "stone typings",
|
|
17
|
-
"test": "
|
|
18
|
-
"test:cvg": "
|
|
17
|
+
"test": "stone test",
|
|
18
|
+
"test:cvg": "stone test --coverage"
|
|
19
19
|
},
|
|
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.
|
|
26
|
-
"@stone-js/node-cli-adapter": "^0.8.7"
|
|
23
|
+
"@stone-js/core": "^0.8.9",
|
|
24
|
+
"@stone-js/use-react": "^0.8.9",
|
|
25
|
+
"@stone-js/browser-adapter": "^0.8.9"
|
|
27
26
|
},
|
|
28
27
|
"devDependencies": {
|
|
29
28
|
"@babel/plugin-proposal-decorators": "^7.25.9",
|
|
30
29
|
"@babel/preset-env": "^7.26.9",
|
|
31
30
|
"@babel/preset-typescript": "^7.27.0",
|
|
32
|
-
"@stone-js/cli": "^0.8.
|
|
31
|
+
"@stone-js/cli": "^0.8.9",
|
|
32
|
+
"@stone-js/testing": "^0.8.9",
|
|
33
33
|
"@types/node": "^24.0.7",
|
|
34
34
|
"@types/react": "^19.0.7",
|
|
35
35
|
"@types/react-dom": "^19.0.3",
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96 96" width="96" height="96" role="img" aria-label="Stone.js">
|
|
2
|
+
<title>Stone.js</title>
|
|
3
|
+
<style>
|
|
4
|
+
.ink { stroke: #1B1613; }
|
|
5
|
+
@media (prefers-color-scheme: dark) { .ink { stroke: #EFE8DC; } }
|
|
6
|
+
</style>
|
|
7
|
+
<defs>
|
|
8
|
+
<linearGradient id="braise" gradientUnits="userSpaceOnUse" x1="22" y1="14" x2="74" y2="26.1">
|
|
9
|
+
<stop offset="0" stop-color="#FFC46B" />
|
|
10
|
+
<stop offset="1" stop-color="#FF5A1F" />
|
|
11
|
+
</linearGradient>
|
|
12
|
+
</defs>
|
|
13
|
+
<path class="ink" d="M 79.96 36.4 A 34 34 0 0 1 53.9 81.5" fill="none" stroke-width="11" stroke-linecap="round" opacity=".85" />
|
|
14
|
+
<path class="ink" d="M 42.1 81.5 A 34 34 0 0 1 16.05 36.4" fill="none" stroke-width="11" stroke-linecap="round" opacity=".85" />
|
|
15
|
+
<path d="M 22 26.1 A 34 34 0 0 1 74 26.1" fill="none" stroke="url(#braise)" stroke-width="11" stroke-linecap="round" />
|
|
16
|
+
</svg>
|
|
@@ -1,91 +1,30 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
} as unknown as ILogger
|
|
22
|
-
|
|
23
|
-
app = FactoryHandler({ logger: mockedLogger })
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
it('should create an application instance', () => {
|
|
27
|
-
// Assert
|
|
28
|
-
expect(app).toBeInstanceOf(Object)
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
it('get head values', async () => {
|
|
32
|
-
// Arrange
|
|
33
|
-
const event = { get: () => 'World' } as unknown as IncomingEvent
|
|
34
|
-
|
|
35
|
-
// Act
|
|
36
|
-
const head = await app.head({ event })
|
|
37
|
-
|
|
38
|
-
// Assert
|
|
39
|
-
expect(head).toHaveProperty('metas')
|
|
40
|
-
expect(head.title).toContain('Welcome to Stone.js')
|
|
41
|
-
expect(head.title).toContain('World')
|
|
42
|
-
expect(head).toHaveProperty('description')
|
|
1
|
+
import { createTestApp, makeIncomingHttpEvent } from '@stone-js/testing'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The whole application, booted in memory and asked a real question.
|
|
5
|
+
*
|
|
6
|
+
* Nothing is mocked and nothing is listed: `createTestApp()` discovers `app/**`, and the event goes
|
|
7
|
+
* through the same kernel production runs. If this passes the application works; if it fails the
|
|
8
|
+
* application is broken. That is the only kind of test worth shipping in a starter — the previous one
|
|
9
|
+
* stubbed out the framework's own decorators, which meant it could pass while nothing worked.
|
|
10
|
+
*/
|
|
11
|
+
describe('Pages', () => {
|
|
12
|
+
it('renders the home page, data included', async () => {
|
|
13
|
+
const app = await createTestApp()
|
|
14
|
+
|
|
15
|
+
const response = await app.send(makeIncomingHttpEvent({ method: 'GET', url: '/?name=Ada' }))
|
|
16
|
+
|
|
17
|
+
expect(response.statusCode).toBe(200)
|
|
18
|
+
// A page is a handler, so a rendered page is a response whose body is HTML. Query that HTML with
|
|
19
|
+
// whatever you already use (happy-dom, jsdom, Testing Library): none is bundled here.
|
|
20
|
+
expect(response.html()).toContain('Ada')
|
|
43
21
|
})
|
|
44
22
|
|
|
45
|
-
it('
|
|
46
|
-
|
|
47
|
-
const expectedMessage = 'Hello World!'
|
|
48
|
-
const event = { get: () => 'World' } as unknown as IncomingEvent
|
|
23
|
+
it('renders without a name', async () => {
|
|
24
|
+
const app = await createTestApp()
|
|
49
25
|
|
|
50
|
-
|
|
51
|
-
const response = app.handle(event) as { message: string }
|
|
26
|
+
const response = await app.send(makeIncomingHttpEvent({ method: 'GET', url: '/' }))
|
|
52
27
|
|
|
53
|
-
|
|
54
|
-
expect(response.message).toBe(expectedMessage)
|
|
55
|
-
expect(mockedLogger.info).toHaveBeenCalledWith(expectedMessage)
|
|
28
|
+
expect(response.statusCode).toBe(200)
|
|
56
29
|
})
|
|
57
|
-
|
|
58
|
-
it('should render the message', () => {
|
|
59
|
-
// Arrange
|
|
60
|
-
const message = 'Hello World!'
|
|
61
|
-
|
|
62
|
-
// Act
|
|
63
|
-
const response = renderToString(app.render({ data: { message } } as any))
|
|
64
|
-
|
|
65
|
-
// Assert
|
|
66
|
-
expect(response).toContain(message)
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
it('should render the Stone.js logo', () => {
|
|
70
|
-
// Act
|
|
71
|
-
const response = renderToString(app.render({ data: { message: 'Hello World!' } } as any))
|
|
72
|
-
|
|
73
|
-
// Assert
|
|
74
|
-
expect(response).toContain('/logo.svg')
|
|
75
|
-
expect(response).toContain('alt="Stone.js"')
|
|
76
|
-
expect(response).toContain('Welcome to')
|
|
77
|
-
expect(response).toContain('Stone.js')
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
it('should get config', () => {
|
|
81
|
-
// Arrange
|
|
82
|
-
const config: any = AppConfig
|
|
83
|
-
const blueprint: any = { is: () => true, set: vi.fn() }
|
|
84
|
-
|
|
85
|
-
// Act
|
|
86
|
-
config.afterConfigure(blueprint)
|
|
87
|
-
|
|
88
|
-
// Assert
|
|
89
|
-
expect(blueprint.set).toHaveBeenCalled()
|
|
90
|
-
})
|
|
91
|
-
})
|
|
30
|
+
})
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { NodeHttp } from '@stone-js/node-http-adapter'
|
|
2
|
-
import { NodeConsole } from '@stone-js/node-cli-adapter'
|
|
3
2
|
import { IncomingEvent, IEventHandler, ILogger, LogLevel, StoneApp } from '@stone-js/core'
|
|
4
3
|
|
|
5
4
|
/**
|
|
@@ -10,7 +9,6 @@ import { IncomingEvent, IEventHandler, ILogger, LogLevel, StoneApp } from '@ston
|
|
|
10
9
|
* @NodeHttp() is used to enable the Node HTTP adapter.
|
|
11
10
|
* @StoneApp() is used to enable the Stone application, it is required.
|
|
12
11
|
*/
|
|
13
|
-
@NodeConsole()
|
|
14
12
|
@NodeHttp({ default: true })
|
|
15
13
|
@StoneApp({ logger: { level: LogLevel.INFO } })
|
|
16
14
|
export class Application implements IEventHandler<IncomingEvent> {
|
|
@@ -14,19 +14,19 @@
|
|
|
14
14
|
"build": "stone build",
|
|
15
15
|
"preview": "stone preview dist/server.mjs",
|
|
16
16
|
"typings": "stone typings",
|
|
17
|
-
"test": "
|
|
18
|
-
"test:cvg": "
|
|
17
|
+
"test": "stone test",
|
|
18
|
+
"test:cvg": "stone test --coverage"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@stone-js/core": "^0.8.
|
|
22
|
-
"@stone-js/node-
|
|
23
|
-
"@stone-js/node-http-adapter": "^0.8.7"
|
|
21
|
+
"@stone-js/core": "^0.8.9",
|
|
22
|
+
"@stone-js/node-http-adapter": "^0.8.9"
|
|
24
23
|
},
|
|
25
24
|
"devDependencies": {
|
|
26
25
|
"@babel/plugin-proposal-decorators": "^7.25.9",
|
|
27
26
|
"@babel/preset-env": "^7.26.9",
|
|
28
27
|
"@babel/preset-typescript": "^7.27.0",
|
|
29
|
-
"@stone-js/cli": "^0.8.
|
|
28
|
+
"@stone-js/cli": "^0.8.9",
|
|
29
|
+
"@stone-js/testing": "^0.8.9",
|
|
30
30
|
"@types/node": "^24.0.7",
|
|
31
31
|
"@vitest/coverage-v8": "^3.0.9",
|
|
32
32
|
"vitest": "^3.0.9"
|
|
@@ -1,61 +1,28 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
// We must mock decorators to lighten the test environment
|
|
14
|
-
vi.mock(import("@stone-js/node-cli-adapter"), async (importOriginal) => {
|
|
15
|
-
const actual = await importOriginal()
|
|
16
|
-
return {
|
|
17
|
-
...actual,
|
|
18
|
-
NodeConsole: vi.fn(() => vi.fn()),
|
|
19
|
-
}
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
// We must mock decorators to lighten the test environment
|
|
23
|
-
vi.mock(import("@stone-js/node-http-adapter"), async (importOriginal) => {
|
|
24
|
-
const actual = await importOriginal()
|
|
25
|
-
return {
|
|
26
|
-
...actual,
|
|
27
|
-
NodeHttp: vi.fn(() => vi.fn()),
|
|
28
|
-
}
|
|
29
|
-
})
|
|
30
|
-
|
|
1
|
+
import { createTestApp, makeIncomingHttpEvent } from '@stone-js/testing'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The whole application, booted in memory and asked a real question.
|
|
5
|
+
*
|
|
6
|
+
* Nothing is mocked and nothing is listed: `createTestApp()` discovers `app/**`, and the event goes
|
|
7
|
+
* through the same kernel production runs. If this passes the application works; if it fails the
|
|
8
|
+
* application is broken. That is the only kind of test worth shipping in a starter — the previous one
|
|
9
|
+
* stubbed out the framework's own decorators, which meant it could pass while nothing worked.
|
|
10
|
+
*/
|
|
31
11
|
describe('Application', () => {
|
|
32
|
-
|
|
33
|
-
|
|
12
|
+
it('answers the caller', async () => {
|
|
13
|
+
const app = await createTestApp()
|
|
34
14
|
|
|
35
|
-
|
|
36
|
-
mockedLogger = {
|
|
37
|
-
info: vi.fn(),
|
|
38
|
-
} as unknown as ILogger
|
|
15
|
+
const response = await app.send(makeIncomingHttpEvent({ method: 'GET', url: '/?name=Ada' }))
|
|
39
16
|
|
|
40
|
-
|
|
17
|
+
expect(response.statusCode).toBe(200)
|
|
18
|
+
expect(response.json()).toMatchObject({ message: expect.stringContaining('Ada') })
|
|
41
19
|
})
|
|
42
20
|
|
|
43
|
-
it('
|
|
44
|
-
|
|
45
|
-
expect(app).toBeInstanceOf(Application)
|
|
46
|
-
})
|
|
21
|
+
it('answers when nobody says who they are', async () => {
|
|
22
|
+
const app = await createTestApp()
|
|
47
23
|
|
|
48
|
-
|
|
49
|
-
// Arrange
|
|
50
|
-
const expectedMessage = 'Hello World! Welcome to Stone.js.'
|
|
51
|
-
const event = { get: () => 'World' } as unknown as IncomingEvent
|
|
24
|
+
const response = await app.send(makeIncomingHttpEvent({ method: 'GET', url: '/' }))
|
|
52
25
|
|
|
53
|
-
|
|
54
|
-
const response = app.handle(event)
|
|
55
|
-
|
|
56
|
-
// Assert
|
|
57
|
-
expect(response.message).toBe(expectedMessage)
|
|
58
|
-
expect(response.framework.name).toBe('Stone.js')
|
|
59
|
-
expect(mockedLogger.info).toHaveBeenCalledWith(expectedMessage)
|
|
26
|
+
expect(response.statusCode).toBe(200)
|
|
60
27
|
})
|
|
61
|
-
})
|
|
28
|
+
})
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { NODE_HTTP_PLATFORM, nodeHttpAdapterBlueprint } from '@stone-js/node-http-adapter'
|
|
2
|
-
import {
|
|
3
|
-
import { defineStoneApp, defineConfig, IBlueprint, ILogger, IncomingEvent, FunctionalEventHandler } from '@stone-js/core'
|
|
2
|
+
import { defineStoneApp, ILogger, IncomingEvent, FunctionalEventHandler } from '@stone-js/core'
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* Create an handler using the factory handler.
|
|
@@ -40,20 +39,9 @@ export const Application = defineStoneApp(
|
|
|
40
39
|
isFactory: true,
|
|
41
40
|
adapter: { platform: NODE_HTTP_PLATFORM }
|
|
42
41
|
},
|
|
43
|
-
[nodeHttpAdapterBlueprint
|
|
42
|
+
[nodeHttpAdapterBlueprint]
|
|
44
43
|
)
|
|
45
44
|
|
|
46
|
-
/**
|
|
47
|
-
* Application configuration.
|
|
48
|
-
*/
|
|
49
|
-
export const AppConfig = defineConfig({
|
|
50
|
-
afterConfigure (blueprint: IBlueprint) {
|
|
51
|
-
if (blueprint.is('stone.adapter.platform', NODE_CONSOLE_PLATFORM)) {
|
|
52
|
-
blueprint.set(defineCommand(FactoryHandler, { name: '*', isFactory: true }))
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
})
|
|
56
|
-
|
|
57
45
|
/**
|
|
58
46
|
* Application options.
|
|
59
47
|
*/
|
|
@@ -14,19 +14,19 @@
|
|
|
14
14
|
"build": "stone build",
|
|
15
15
|
"preview": "stone preview dist/server.mjs",
|
|
16
16
|
"typings": "stone typings",
|
|
17
|
-
"test": "
|
|
18
|
-
"test:cvg": "
|
|
17
|
+
"test": "stone test",
|
|
18
|
+
"test:cvg": "stone test --coverage"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@stone-js/core": "^0.8.
|
|
22
|
-
"@stone-js/node-
|
|
23
|
-
"@stone-js/node-http-adapter": "^0.8.7"
|
|
21
|
+
"@stone-js/core": "^0.8.9",
|
|
22
|
+
"@stone-js/node-http-adapter": "^0.8.9"
|
|
24
23
|
},
|
|
25
24
|
"devDependencies": {
|
|
26
25
|
"@babel/plugin-proposal-decorators": "^7.25.9",
|
|
27
26
|
"@babel/preset-env": "^7.26.9",
|
|
28
27
|
"@babel/preset-typescript": "^7.27.0",
|
|
29
|
-
"@stone-js/cli": "^0.8.
|
|
28
|
+
"@stone-js/cli": "^0.8.9",
|
|
29
|
+
"@stone-js/testing": "^0.8.9",
|
|
30
30
|
"@types/node": "^24.0.7",
|
|
31
31
|
"@vitest/coverage-v8": "^3.0.9",
|
|
32
32
|
"vitest": "^3.0.9"
|