@api-client/ui 0.1.1 → 0.1.3
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/build/src/bindings/base/IoThread.d.ts +2 -2
- package/build/src/bindings/base/IoThread.d.ts.map +1 -1
- package/build/src/bindings/base/IoThread.js +4 -4
- package/build/src/bindings/base/IoThread.js.map +1 -1
- package/build/src/bindings/base/PlatformBindings.js +2 -2
- package/build/src/bindings/base/PlatformBindings.js.map +1 -1
- package/build/src/core/Activity.d.ts +3 -3
- package/build/src/core/Activity.d.ts.map +1 -1
- package/build/src/core/Activity.js +8 -7
- package/build/src/core/Activity.js.map +1 -1
- package/build/src/core/ActivityManager.d.ts +9 -0
- package/build/src/core/ActivityManager.d.ts.map +1 -1
- package/build/src/core/ActivityManager.js +57 -12
- package/build/src/core/ActivityManager.js.map +1 -1
- package/build/src/core/Application.d.ts +1 -0
- package/build/src/core/Application.d.ts.map +1 -1
- package/build/src/core/Application.js +3 -0
- package/build/src/core/Application.js.map +1 -1
- package/build/src/core/Fragment.d.ts +9 -3
- package/build/src/core/Fragment.d.ts.map +1 -1
- package/build/src/core/Fragment.js +17 -8
- package/build/src/core/Fragment.js.map +1 -1
- package/build/src/core/FragmentManager.d.ts +17 -0
- package/build/src/core/FragmentManager.d.ts.map +1 -1
- package/build/src/core/FragmentManager.js +58 -2
- package/build/src/core/FragmentManager.js.map +1 -1
- package/build/src/core/renderer/Renderer.d.ts +4 -1
- package/build/src/core/renderer/Renderer.d.ts.map +1 -1
- package/build/src/core/renderer/Renderer.js +3 -0
- package/build/src/core/renderer/Renderer.js.map +1 -1
- package/build/src/elements/data-table/DataTable.d.ts +7 -0
- package/build/src/elements/data-table/DataTable.d.ts.map +1 -1
- package/build/src/elements/data-table/DataTable.js +8 -0
- package/build/src/elements/data-table/DataTable.js.map +1 -1
- package/package.json +1 -1
- package/src/bindings/base/IoThread.ts +7 -7
- package/src/bindings/base/PlatformBindings.ts +3 -3
- package/src/core/Activity.ts +8 -7
- package/src/core/ActivityManager.ts +58 -12
- package/src/core/Application.ts +4 -0
- package/src/core/Fragment.ts +18 -8
- package/src/core/FragmentManager.ts +61 -3
- package/src/core/renderer/Renderer.ts +4 -1
- package/src/elements/data-table/DataTable.ts +7 -0
- package/test/core/activity.spec.ts +235 -2
- package/test/core/activity_manager.spec.ts +544 -0
- package/test/core/application.spec.ts +218 -0
- package/test/core/fragment.spec.ts +472 -0
- package/test/core/fragment_manager.spec.ts +404 -0
- package/web-test-runner.config.js +1 -1
|
@@ -37,6 +37,15 @@ export class FragmentManager {
|
|
|
37
37
|
this.host = host
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
async onDestroy(): Promise<void> {
|
|
41
|
+
// Clean up all fragments when the host is destroyed
|
|
42
|
+
for (const [key, fragment] of this.fragments) {
|
|
43
|
+
await fragment.onDestroy()
|
|
44
|
+
this.removeFragmentStyles(fragment)
|
|
45
|
+
this.fragments.delete(key)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
40
49
|
/**
|
|
41
50
|
* Attaches a fragment to a parent Activity or Fragment
|
|
42
51
|
*
|
|
@@ -101,6 +110,55 @@ export class FragmentManager {
|
|
|
101
110
|
} else {
|
|
102
111
|
this.host.requestUpdate({ app: false, activity: true })
|
|
103
112
|
}
|
|
113
|
+
this.setFragmentStyles(fragment)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Sets styles for a fragment by adding a class to the root element.
|
|
118
|
+
* This is useful for applying specific styles to fragments.
|
|
119
|
+
*
|
|
120
|
+
* The root element is either fragment's render root or the document element.
|
|
121
|
+
* @param fragment The fragment to set styles for.
|
|
122
|
+
*/
|
|
123
|
+
setFragmentStyles(fragment: Fragment): void {
|
|
124
|
+
const root = fragment.renderer.renderRoot ?? document?.documentElement
|
|
125
|
+
if (!root) {
|
|
126
|
+
return
|
|
127
|
+
}
|
|
128
|
+
const classes: string[] = []
|
|
129
|
+
const ctor = fragment.constructor
|
|
130
|
+
classes.push(ctor.name.toLowerCase())
|
|
131
|
+
if (typeof fragment.rootClass === 'function') {
|
|
132
|
+
const className = fragment.rootClass()
|
|
133
|
+
if (className) {
|
|
134
|
+
classes.push(className)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
root.classList.add(...classes)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Removes styles for a fragment by removing a class from the root element.
|
|
142
|
+
* This is useful for cleaning up styles when a fragment is no longer visible.
|
|
143
|
+
*
|
|
144
|
+
* The root element is either fragment's render root or the document element.
|
|
145
|
+
* @param fragment The fragment to remove styles for.
|
|
146
|
+
*/
|
|
147
|
+
removeFragmentStyles(fragment: Fragment): void {
|
|
148
|
+
const root = fragment.renderer.renderRoot ?? document?.documentElement
|
|
149
|
+
if (!root) {
|
|
150
|
+
return
|
|
151
|
+
}
|
|
152
|
+
const classes: string[] = []
|
|
153
|
+
const ctor = fragment.constructor
|
|
154
|
+
classes.push(ctor.name.toLowerCase())
|
|
155
|
+
if (typeof fragment.rootClass === 'function') {
|
|
156
|
+
const className = fragment.rootClass()
|
|
157
|
+
if (className) {
|
|
158
|
+
classes.push(className)
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
root.classList.remove(...classes)
|
|
104
162
|
}
|
|
105
163
|
|
|
106
164
|
/**
|
|
@@ -119,7 +177,7 @@ export class FragmentManager {
|
|
|
119
177
|
fragment.state = FragmentState.Paused
|
|
120
178
|
await fragment.onStop()
|
|
121
179
|
fragment.state = FragmentState.Stopped
|
|
122
|
-
|
|
180
|
+
this.removeFragmentStyles(fragment)
|
|
123
181
|
// if necessary, you would remove its template content from the DOM
|
|
124
182
|
// however, with rendering logic encapsulated in Fragment, this isn't needed here
|
|
125
183
|
}
|
|
@@ -133,7 +191,7 @@ export class FragmentManager {
|
|
|
133
191
|
if (!fragment || fragment.state !== FragmentState.Resumed) {
|
|
134
192
|
return nothing
|
|
135
193
|
}
|
|
136
|
-
return fragment
|
|
194
|
+
return fragment.render() || nothing
|
|
137
195
|
}
|
|
138
196
|
|
|
139
197
|
updateActiveFragments(): void {
|
|
@@ -151,7 +209,7 @@ export class FragmentManager {
|
|
|
151
209
|
*/
|
|
152
210
|
findByRequestCode(requestCode: number): Fragment | null {
|
|
153
211
|
for (const [, fragment] of this.fragments) {
|
|
154
|
-
if (fragment.
|
|
212
|
+
if (fragment.hasRequestCode(requestCode)) {
|
|
155
213
|
return fragment
|
|
156
214
|
}
|
|
157
215
|
}
|
|
@@ -9,7 +9,10 @@ export const renderFn = Symbol('renderFn')
|
|
|
9
9
|
* A class that manages rendering of the application screen.
|
|
10
10
|
*/
|
|
11
11
|
export abstract class Renderer {
|
|
12
|
-
|
|
12
|
+
/**
|
|
13
|
+
* The root element where the activity or fragment will be rendered.
|
|
14
|
+
*/
|
|
15
|
+
renderRoot: HTMLElement | null = null
|
|
13
16
|
|
|
14
17
|
/**
|
|
15
18
|
* @type A promise resolved when the render finished.
|
|
@@ -235,10 +235,17 @@ export abstract class DataTable<T extends object> extends LitElement {
|
|
|
235
235
|
/**
|
|
236
236
|
* The name of the property to sort the table by. If set, the table will
|
|
237
237
|
* automatically indicate the sortable header.
|
|
238
|
+
* @attribute
|
|
238
239
|
* @default undefined
|
|
239
240
|
*/
|
|
240
241
|
@property({ type: String }) accessor sort: string | undefined
|
|
241
242
|
|
|
243
|
+
/**
|
|
244
|
+
* The direction of the sort. Can be "asc" (ascending) or "desc" (descending).
|
|
245
|
+
* If `sort` is set, this property will be used to determine the sort direction.
|
|
246
|
+
* @attribute
|
|
247
|
+
* @default undefined
|
|
248
|
+
*/
|
|
242
249
|
@property({ type: String }) accessor sortDirection: 'asc' | 'desc' | undefined
|
|
243
250
|
|
|
244
251
|
protected lastSelectedIndex = -1
|
|
@@ -3,24 +3,39 @@ import { Activity } from '../../src/core/Activity.js'
|
|
|
3
3
|
import { Application } from '../../src/core/Application.js'
|
|
4
4
|
import { IntentResult, ActivityState } from '../../src/core/ActivityManager.js'
|
|
5
5
|
import { fixture, html } from '@open-wc/testing'
|
|
6
|
+
import { EventTypes } from '../../src/events/EventTypes.js'
|
|
6
7
|
import sinon from 'sinon'
|
|
8
|
+
import { nothing } from 'lit'
|
|
9
|
+
import { Fragment } from '../../src/core/Fragment.js'
|
|
10
|
+
import type { ActivityDetail } from '../../src/events/IntentEvents.js'
|
|
7
11
|
|
|
8
12
|
class TestActivity extends Activity {
|
|
9
13
|
static override action = 'test-activity'
|
|
10
14
|
}
|
|
11
15
|
|
|
16
|
+
class TestActivity2 extends Activity {
|
|
17
|
+
static override action = 'test-activity2'
|
|
18
|
+
}
|
|
19
|
+
|
|
12
20
|
class TestApplication extends Application {
|
|
13
21
|
override async onStart(): Promise<void> {
|
|
14
22
|
this.manager.registerActivity(TestActivity.action, TestActivity)
|
|
23
|
+
this.manager.registerActivity(TestActivity2.action, TestActivity2)
|
|
24
|
+
// The creation below is for tests that require an activity to be present in the manager
|
|
25
|
+
// e.g. for setResult, getApplication, getActivity, hasRequestCode, onActivityResult
|
|
26
|
+
// For other tests, a new TestActivity is created directly.
|
|
27
|
+
// This helps isolate tests for lifecycle events from manager interactions.
|
|
15
28
|
await this.manager.createActivity({ action: TestActivity.action, data: { test: true } })
|
|
16
29
|
}
|
|
17
30
|
}
|
|
18
31
|
|
|
32
|
+
class TestFragment extends Fragment {}
|
|
33
|
+
|
|
19
34
|
async function basicFixture(): Promise<HTMLElement> {
|
|
20
35
|
return fixture(html`<div id="app"></div>`)
|
|
21
36
|
}
|
|
22
37
|
|
|
23
|
-
describe('Events', () => {
|
|
38
|
+
describe('Activity Lifecycle and Events', () => {
|
|
24
39
|
let application: Application
|
|
25
40
|
let activity: Activity
|
|
26
41
|
|
|
@@ -28,6 +43,7 @@ describe('Events', () => {
|
|
|
28
43
|
const renderRoot = await basicFixture()
|
|
29
44
|
application = new TestApplication(renderRoot)
|
|
30
45
|
activity = new TestActivity(application)
|
|
46
|
+
// Note: application.onStart() is not called here to isolate Activity's own lifecycle methods.
|
|
31
47
|
})
|
|
32
48
|
|
|
33
49
|
it('dispatches the activity:create event', async () => {
|
|
@@ -79,6 +95,12 @@ describe('Events', () => {
|
|
|
79
95
|
assert.isTrue(spy.calledOnce)
|
|
80
96
|
})
|
|
81
97
|
|
|
98
|
+
it('calls manager.onDestroy when activity is destroyed', async () => {
|
|
99
|
+
const spy = sinon.spy(activity['manager'], 'onDestroy')
|
|
100
|
+
await activity.onDestroy()
|
|
101
|
+
assert.isTrue(spy.calledOnce)
|
|
102
|
+
})
|
|
103
|
+
|
|
82
104
|
it('sets the default state', async () => {
|
|
83
105
|
assert.equal(activity.state, ActivityState.Initialized)
|
|
84
106
|
})
|
|
@@ -86,6 +108,22 @@ describe('Events', () => {
|
|
|
86
108
|
it('sets the default resultCode', async () => {
|
|
87
109
|
assert.equal(activity.resultCode, IntentResult.RESULT_CANCELED)
|
|
88
110
|
})
|
|
111
|
+
|
|
112
|
+
it('onNewIntent can be called', async () => {
|
|
113
|
+
assert.doesNotThrow(async () => {
|
|
114
|
+
await activity.onNewIntent({ action: 'test' })
|
|
115
|
+
})
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
it('onFirstRender can be called', () => {
|
|
119
|
+
assert.doesNotThrow(() => {
|
|
120
|
+
activity.onFirstRender()
|
|
121
|
+
})
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
it('render returns nothing by default', () => {
|
|
125
|
+
assert.strictEqual(activity.render(), nothing)
|
|
126
|
+
})
|
|
89
127
|
})
|
|
90
128
|
|
|
91
129
|
describe('setResult()', () => {
|
|
@@ -95,6 +133,7 @@ describe('setResult()', () => {
|
|
|
95
133
|
beforeEach(async () => {
|
|
96
134
|
const renderRoot = await basicFixture()
|
|
97
135
|
application = new TestApplication(renderRoot)
|
|
136
|
+
// application.onStart() creates an activity instance via the manager
|
|
98
137
|
await application.onStart()
|
|
99
138
|
activity = application.manager.findActiveActivity(TestActivity.action) as Activity
|
|
100
139
|
})
|
|
@@ -107,6 +146,21 @@ describe('setResult()', () => {
|
|
|
107
146
|
})
|
|
108
147
|
})
|
|
109
148
|
|
|
149
|
+
describe('getResult()', () => {
|
|
150
|
+
let application: Application
|
|
151
|
+
let activity: Activity
|
|
152
|
+
|
|
153
|
+
beforeEach(async () => {
|
|
154
|
+
const renderRoot = await basicFixture()
|
|
155
|
+
application = new TestApplication(renderRoot)
|
|
156
|
+
activity = new TestActivity(application) // Test with a fresh activity not from manager
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
it('returns undefined by default', () => {
|
|
160
|
+
assert.isUndefined(activity.getResult())
|
|
161
|
+
})
|
|
162
|
+
})
|
|
163
|
+
|
|
110
164
|
describe('getApplication()', () => {
|
|
111
165
|
let application: Application
|
|
112
166
|
let activity: Activity
|
|
@@ -114,6 +168,7 @@ describe('getApplication()', () => {
|
|
|
114
168
|
beforeEach(async () => {
|
|
115
169
|
const renderRoot = await basicFixture()
|
|
116
170
|
application = new TestApplication(renderRoot)
|
|
171
|
+
// application.onStart() creates an activity instance via the manager
|
|
117
172
|
await application.onStart()
|
|
118
173
|
activity = application.manager.findActiveActivity(TestActivity.action) as Activity
|
|
119
174
|
})
|
|
@@ -130,6 +185,7 @@ describe('getActivity()', () => {
|
|
|
130
185
|
beforeEach(async () => {
|
|
131
186
|
const renderRoot = await basicFixture()
|
|
132
187
|
application = new TestApplication(renderRoot)
|
|
188
|
+
// application.onStart() creates an activity instance via the manager
|
|
133
189
|
await application.onStart()
|
|
134
190
|
activity = application.manager.findActiveActivity(TestActivity.action) as Activity
|
|
135
191
|
})
|
|
@@ -146,6 +202,7 @@ describe('hasRequestCode()', () => {
|
|
|
146
202
|
beforeEach(async () => {
|
|
147
203
|
const renderRoot = await basicFixture()
|
|
148
204
|
application = new TestApplication(renderRoot)
|
|
205
|
+
// application.onStart() creates an activity instance via the manager
|
|
149
206
|
await application.onStart()
|
|
150
207
|
activity = application.manager.findActiveActivity(TestActivity.action) as Activity
|
|
151
208
|
})
|
|
@@ -155,7 +212,7 @@ describe('hasRequestCode()', () => {
|
|
|
155
212
|
assert.isTrue(activity.hasRequestCode(requestCode))
|
|
156
213
|
})
|
|
157
214
|
|
|
158
|
-
it('returns
|
|
215
|
+
it('returns false when request code is not pending', async () => {
|
|
159
216
|
await activity.startActivityForResult({ action: TestActivity.action })
|
|
160
217
|
assert.isFalse(activity.hasRequestCode(1))
|
|
161
218
|
})
|
|
@@ -168,6 +225,7 @@ describe('onActivityResult()', () => {
|
|
|
168
225
|
beforeEach(async () => {
|
|
169
226
|
const renderRoot = await basicFixture()
|
|
170
227
|
application = new TestApplication(renderRoot)
|
|
228
|
+
// application.onStart() creates an activity instance via the manager
|
|
171
229
|
await application.onStart()
|
|
172
230
|
activity = application.manager.findActiveActivity(TestActivity.action) as Activity
|
|
173
231
|
})
|
|
@@ -177,4 +235,179 @@ describe('onActivityResult()', () => {
|
|
|
177
235
|
await activity.onActivityResult(2, IntentResult.RESULT_OK, { action: 'test' })
|
|
178
236
|
})
|
|
179
237
|
})
|
|
238
|
+
|
|
239
|
+
it('removes request code if present', async () => {
|
|
240
|
+
const requestCode = await activity.startActivityForResult({ action: TestActivity.action })
|
|
241
|
+
assert.isTrue(activity.hasRequestCode(requestCode))
|
|
242
|
+
await activity.onActivityResult(requestCode, IntentResult.RESULT_OK, { action: 'test' })
|
|
243
|
+
assert.isFalse(activity.hasRequestCode(requestCode))
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
it('forwards to fragment if fragment has request code', async () => {
|
|
247
|
+
const fragment = new TestFragment()
|
|
248
|
+
const onActivityResultStub = sinon.spy(fragment, 'onActivityResult')
|
|
249
|
+
sinon.stub(fragment, 'hasRequestCode').returns(true)
|
|
250
|
+
sinon.stub(activity['manager'], 'findByRequestCode').returns(fragment as unknown as Fragment)
|
|
251
|
+
const requestCode = 123
|
|
252
|
+
await activity.onActivityResult(requestCode, IntentResult.RESULT_OK, { action: 'test' })
|
|
253
|
+
assert.isTrue(onActivityResultStub.calledOnceWith(requestCode, IntentResult.RESULT_OK, { action: 'test' }))
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
it('logs info if not handled by fragment and is base Activity', async () => {
|
|
257
|
+
sinon.stub(activity['manager'], 'findByRequestCode').returns(null)
|
|
258
|
+
const consoleSpy = sinon.spy(console, 'info')
|
|
259
|
+
const requestCode = 123
|
|
260
|
+
// Ensure the activity instance is exactly Activity, not a subclass that might override onActivityResult
|
|
261
|
+
Object.setPrototypeOf(activity, Activity.prototype)
|
|
262
|
+
|
|
263
|
+
await activity.onActivityResult(requestCode, IntentResult.RESULT_OK, { action: 'test-intent' })
|
|
264
|
+
assert.isTrue(
|
|
265
|
+
consoleSpy.calledWith(
|
|
266
|
+
`Activity#onActivityResult not implemented. Request code: ${requestCode}, result code: ${IntentResult.RESULT_OK}`,
|
|
267
|
+
{ action: 'test-intent' }
|
|
268
|
+
)
|
|
269
|
+
)
|
|
270
|
+
consoleSpy.restore()
|
|
271
|
+
})
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
describe('Activity Actions', () => {
|
|
275
|
+
let application: Application
|
|
276
|
+
let activity: Activity
|
|
277
|
+
|
|
278
|
+
beforeEach(async () => {
|
|
279
|
+
const renderRoot = await basicFixture()
|
|
280
|
+
application = new TestApplication(renderRoot)
|
|
281
|
+
// For these tests, we often need a fresh activity instance not necessarily from the manager's stack
|
|
282
|
+
// to avoid side effects from application.onStart() unless specifically testing manager interactions.
|
|
283
|
+
activity = new TestActivity(application)
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
it('startActivity calls application manager startActivity', async () => {
|
|
287
|
+
const intent = { action: TestActivity2.action }
|
|
288
|
+
const spy = sinon.spy(application.manager, 'startActivity')
|
|
289
|
+
await application.onStart() // Ensure the manager is initialized
|
|
290
|
+
await activity.startActivity(intent)
|
|
291
|
+
assert.isTrue(spy.calledOnceWith(intent))
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
it('startActivityForResult calls application manager startActivityForResult and stores request code', async () => {
|
|
295
|
+
const intent = { action: TestActivity2.action }
|
|
296
|
+
const managerSpy = sinon.spy(application.manager, 'startActivityForResult')
|
|
297
|
+
const createCodeSpy = sinon.stub(application.manager, 'createRequestCode').returns(999)
|
|
298
|
+
await application.onStart() // Ensure the manager is initialized
|
|
299
|
+
const requestCode = await activity.startActivityForResult(intent)
|
|
300
|
+
|
|
301
|
+
assert.equal(requestCode, 999)
|
|
302
|
+
assert.isTrue(managerSpy.calledOnceWith(intent, 999))
|
|
303
|
+
assert.isTrue(activity.hasRequestCode(999))
|
|
304
|
+
|
|
305
|
+
createCodeSpy.restore()
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
it('finish calls application manager finishActivity', async () => {
|
|
309
|
+
const spy = sinon.spy(application.manager, 'finishActivity')
|
|
310
|
+
await activity.finish()
|
|
311
|
+
assert.isTrue(spy.calledOnceWith(activity))
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
it('requestUpdate calls parent application requestUpdate and manager updateActiveFragments', () => {
|
|
315
|
+
const appSpy = sinon.spy(application, 'requestUpdate')
|
|
316
|
+
const managerSpy = sinon.spy(activity['manager'], 'updateActiveFragments')
|
|
317
|
+
const opts = { app: false, activity: true, fragment: false }
|
|
318
|
+
activity.requestUpdate(opts)
|
|
319
|
+
assert.isTrue(appSpy.calledOnceWith(opts))
|
|
320
|
+
assert.isTrue(managerSpy.calledOnce)
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
it('requestUpdate defaults to updating activity fragments', () => {
|
|
324
|
+
const managerSpy = sinon.spy(activity['manager'], 'updateActiveFragments')
|
|
325
|
+
activity.requestUpdate() // No opts, so activity should be true by default
|
|
326
|
+
assert.isTrue(managerSpy.calledOnce)
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
it('requestUpdate respects activity: false in opts', () => {
|
|
330
|
+
const managerSpy = sinon.spy(activity['manager'], 'updateActiveFragments')
|
|
331
|
+
activity.requestUpdate({ activity: false })
|
|
332
|
+
assert.isFalse(managerSpy.called)
|
|
333
|
+
})
|
|
334
|
+
})
|
|
335
|
+
|
|
336
|
+
describe('Fragment Management', () => {
|
|
337
|
+
let application: Application
|
|
338
|
+
let activity: Activity
|
|
339
|
+
|
|
340
|
+
beforeEach(async () => {
|
|
341
|
+
const renderRoot = await basicFixture()
|
|
342
|
+
application = new TestApplication(renderRoot)
|
|
343
|
+
activity = new TestActivity(application)
|
|
344
|
+
})
|
|
345
|
+
|
|
346
|
+
it('addFragment calls manager.attachFragment', async () => {
|
|
347
|
+
const spy = sinon.spy(activity['manager'], 'attachFragment')
|
|
348
|
+
const fragment = new TestFragment()
|
|
349
|
+
const data = { info: 'test' }
|
|
350
|
+
await activity.addFragment('testKey', fragment, data)
|
|
351
|
+
assert.isTrue(spy.calledOnceWith('testKey', fragment, activity, data))
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
it('showFragment calls manager.showFragment', async () => {
|
|
355
|
+
const spy = sinon.spy(activity['manager'], 'showFragment')
|
|
356
|
+
activity.addFragment('testKey', new TestFragment()) // Ensure fragment exists
|
|
357
|
+
const rootEl = document.createElement('div')
|
|
358
|
+
await activity.showFragment('testKey', rootEl)
|
|
359
|
+
assert.isTrue(spy.calledOnceWith('testKey', rootEl))
|
|
360
|
+
})
|
|
361
|
+
|
|
362
|
+
it('hideFragment calls manager.hideFragment if fragment exists', async () => {
|
|
363
|
+
const fragment = new TestFragment()
|
|
364
|
+
sinon.stub(activity['manager'], 'findFragment').returns(fragment)
|
|
365
|
+
const spy = sinon.spy(activity['manager'], 'hideFragment')
|
|
366
|
+
await activity.hideFragment('testKey')
|
|
367
|
+
assert.isTrue(spy.calledOnceWith(fragment))
|
|
368
|
+
})
|
|
369
|
+
|
|
370
|
+
it('hideFragment does not call manager.hideFragment if fragment does not exist', async () => {
|
|
371
|
+
sinon.stub(activity['manager'], 'findFragment').returns(undefined)
|
|
372
|
+
const spy = sinon.spy(activity['manager'], 'hideFragment')
|
|
373
|
+
await activity.hideFragment('testKey')
|
|
374
|
+
assert.isFalse(spy.called)
|
|
375
|
+
})
|
|
376
|
+
})
|
|
377
|
+
|
|
378
|
+
describe('handleIntentEvent', () => {
|
|
379
|
+
let activity: Activity
|
|
380
|
+
|
|
381
|
+
beforeEach(async () => {
|
|
382
|
+
const renderRoot = await basicFixture()
|
|
383
|
+
const application = new TestApplication(renderRoot)
|
|
384
|
+
await application.onStart() // Ensure the manager is initialized
|
|
385
|
+
activity = new TestActivity(application)
|
|
386
|
+
})
|
|
387
|
+
|
|
388
|
+
it('handles startActivityForResult event', async () => {
|
|
389
|
+
const startActivityForResultSpy = sinon.spy(activity, 'startActivityForResult')
|
|
390
|
+
const detail = { intent: { action: TestActivity2.action }, onResult: () => {} }
|
|
391
|
+
const event = new CustomEvent(EventTypes.Intent.startActivityForResult, { detail })
|
|
392
|
+
await activity.handleIntentEvent(event)
|
|
393
|
+
assert.isTrue(startActivityForResultSpy.calledOnceWith(detail.intent))
|
|
394
|
+
})
|
|
395
|
+
|
|
396
|
+
it('handles startActivity event', async () => {
|
|
397
|
+
const startActivitySpy = sinon.spy(activity, 'startActivity')
|
|
398
|
+
const detail = { intent: { action: TestActivity2.action } }
|
|
399
|
+
const event = new CustomEvent(EventTypes.Intent.startActivity, { detail })
|
|
400
|
+
await activity.handleIntentEvent(event)
|
|
401
|
+
assert.isTrue(startActivitySpy.calledOnceWith(detail.intent))
|
|
402
|
+
})
|
|
403
|
+
|
|
404
|
+
it('throws error for unrecognized event type', async () => {
|
|
405
|
+
const event = new CustomEvent('unknown-event', { detail: {} as unknown as ActivityDetail })
|
|
406
|
+
try {
|
|
407
|
+
await activity.handleIntentEvent(event)
|
|
408
|
+
assert.fail('Should have thrown an error')
|
|
409
|
+
} catch (e) {
|
|
410
|
+
assert.equal((e as Error).message, `Unrecognized intent event: unknown-event`)
|
|
411
|
+
}
|
|
412
|
+
})
|
|
180
413
|
})
|