also looking at this
fix(android): tie a ready mark to its own navigation, and inject a document once
#239
5 files
+159
-3
| @@ -348,7 +348,7 @@ describe('Craft Android builder', () => { | ||
| 348 | 348 | expect(activity).toContain('craftBridge.restoreDeepLinks(savedInstanceState)') |
| 349 | 349 | |
| 350 | 350 | // Flushed in the injection's completion, after the page's replay exists. |
| 351 | const injected = bridge.slice(bridge.indexOf('webView.evaluateJavascript(script) {')) | |
| 351 | const injected = bridge.slice(bridge.indexOf('webView.evaluateJavascript(onlyOnce(script)) {')) | |
| 352 | 352 | const flush = injected.slice(0, injected.indexOf('fun markBridgeLoading()')) |
| 353 | 353 | expect(flush.indexOf('bridgeReady = true')).toBeLessThan(flush.indexOf('links.forEach { (url, initial) -> dispatchDeepLink(url, initial) }')) |
| 354 | 354 | |
| @@ -413,6 +413,31 @@ describe('Craft Android builder', () => { | ||
| 413 | 413 | expect(bridge).toContain('sendEvent("craftSpeechEnd", emptyMap())') |
| 414 | 414 | }) |
| 415 | 415 | |
| 416 | it('marks ready only the page its injection ran in, and injects a document once', async () => { | |
| 417 | // #228, two halves. The evaluateJavascript completion is posted, so it | |
| 418 | // can land after the next navigation has started and mark a page that | |
| 419 | // was never injected as ready — flushing the deep-link and shortcut | |
| 420 | // queues into a document with no bridge. And onPageFinished arrives | |
| 421 | // without onPageStarted for a same-document navigation, so the script | |
| 422 | // could run twice in one document. | |
| 423 | const output = mkdtempSync(join(tmpdir(), 'craft-android-injection-')) | |
| 424 | await init({ name: 'WildLoop', packageName: 'org.wildloop.app', output }) | |
| 425 | ||
| 426 | const bridge = readFileSync(join(output, 'app/src/main/java/org/wildloop/app/CraftBridge.kt'), 'utf8') | |
| 427 | expect(bridge).toContain('private var navigationGeneration = 0') | |
| 428 | expect(bridge).toContain('navigationGeneration += 1') | |
| 429 | expect(bridge).toContain('val generation = navigationGeneration') | |
| 430 | expect(bridge).toContain('if (generation != navigationGeneration) return@evaluateJavascript') | |
| 431 | // The generation is taken before the script is handed over, and compared | |
| 432 | // after; either order alone leaves the race open. | |
| 433 | expect(bridge.indexOf('val generation = navigationGeneration')) | |
| 434 | .toBeLessThan(bridge.indexOf('if (generation != navigationGeneration)')) | |
| 435 | ||
| 436 | expect(bridge).toContain('private fun onlyOnce(script: String): String') | |
| 437 | expect(bridge).toContain('evaluateJavascript(onlyOnce(script))') | |
| 438 | expect(bridge).toContain('if (!window.__craftBridgeInstalled)') | |
| 439 | }) | |
| 440 | ||
| 416 | 441 | it('routes external Activity results back to every pending media promise', async () => { |
| 417 | 442 | const output = mkdtempSync(join(tmpdir(), 'craft-android-activity-results-')) |
| 418 | 443 | await init({ name: 'WildLoop', packageName: 'org.wildloop.app', output }) |