@robotical/martyblocksjr 4.2.10 → 4.2.11
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/editions/free/src/30d09ba32a17082ef820.hex +26350 -0
- package/editions/free/src/app.bundle.js +1 -1
- package/editions/free/src/app.bundle.js.LICENSE.txt +15 -0
- package/editions/free/src/assets/blockicons/microbitbuttona.svg +7 -1
- package/editions/free/src/assets/blockicons/microbitbuttonab.svg +8 -1
- package/editions/free/src/assets/blockicons/microbitbuttonb.svg +7 -1
- package/editions/free/src/assets/blockicons/microbitdisplayclear.svg +9 -25
- package/editions/free/src/assets/blockicons/microbitdisplayhappy.svg +13 -31
- package/editions/free/src/assets/blockicons/microbitdisplayheart.svg +13 -37
- package/editions/free/src/assets/blockicons/microbitdisplaytext.svg +10 -25
- package/editions/free/src/assets/blockicons/microbittiltleft.svg +1 -1
- package/editions/free/src/assets/blockicons/microbittiltright.svg +1 -1
- package/editions/free/src/assets/connection/remove_extension-default.svg +5 -0
- package/editions/free/src/css/editor.css +171 -5
- package/editions/free/src/css/editorleftpanel.css +87 -12
- package/editions/free/src/localizations/bg.json +12 -12
- package/editions/free/src/localizations/ca.json +12 -12
- package/editions/free/src/localizations/cs.json +12 -12
- package/editions/free/src/localizations/cy.json +12 -12
- package/editions/free/src/localizations/da.json +12 -12
- package/editions/free/src/localizations/de.json +12 -12
- package/editions/free/src/localizations/el.json +12 -12
- package/editions/free/src/localizations/en.json +34 -14
- package/editions/free/src/localizations/es.json +12 -12
- package/editions/free/src/localizations/fi.json +12 -12
- package/editions/free/src/localizations/fr.json +12 -12
- package/editions/free/src/localizations/it.json +12 -12
- package/editions/free/src/localizations/ja.json +12 -12
- package/editions/free/src/localizations/ko.json +12 -12
- package/editions/free/src/localizations/nl.json +12 -12
- package/editions/free/src/localizations/no.json +12 -12
- package/editions/free/src/localizations/pl.json +12 -12
- package/editions/free/src/localizations/pt-br.json +12 -12
- package/editions/free/src/localizations/pt.json +12 -12
- package/editions/free/src/localizations/sv.json +12 -12
- package/editions/free/src/localizations/th.json +12 -12
- package/editions/free/src/localizations/tr.json +12 -12
- package/editions/free/src/localizations/uk.json +12 -12
- package/editions/free/src/localizations/zh-cn.json +12 -12
- package/editions/free/src/localizations/zh-tw.json +12 -12
- package/package.json +4 -1
- package/scripts/prepare-microbit-hex.mjs +47 -0
- package/tests/BlockGuideRegistry.test.js +4 -2
- package/tests/MicroBitBlockDescriptions.test.js +28 -0
- package/tests/MicroBitBlockOptions.test.js +34 -0
- package/tests/MicroBitButtonIcons.test.js +24 -0
- package/tests/MicroBitDisplayIcons.test.js +64 -0
- package/tests/e2e/chromium-79-smoke.test.js +126 -10
- package/tests/e2e/marty-connection-ui.e2e.test.js +152 -4
- package/tests/e2e/microbit-updater.test.js +126 -0
- package/vitest.config.js +1 -0
- package/webpack.config.js +4 -0
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
2
|
|
|
3
|
-
const { newHTMLMock } = vi.hoisted(() => ({
|
|
3
|
+
const { microBitUpdaterMocks, newHTMLMock } = vi.hoisted(() => ({
|
|
4
|
+
microBitUpdaterMocks: {
|
|
5
|
+
isSupported: vi.fn(() => false),
|
|
6
|
+
selectAndUpdate: vi.fn()
|
|
7
|
+
},
|
|
4
8
|
newHTMLMock: vi.fn()
|
|
5
9
|
}));
|
|
6
10
|
|
|
@@ -30,7 +34,8 @@ vi.mock('@/painteditor/Paint', () => ({ default: {} }));
|
|
|
30
34
|
vi.mock('@/utils/Events', () => ({ default: {} }));
|
|
31
35
|
vi.mock('@/utils/Localization', () => ({
|
|
32
36
|
default: {
|
|
33
|
-
localize: key => key
|
|
37
|
+
localize: key => key,
|
|
38
|
+
localizeOptional: key => key
|
|
34
39
|
}
|
|
35
40
|
}));
|
|
36
41
|
vi.mock('@/utils/ScratchAudio', () => ({ default: {} }));
|
|
@@ -127,6 +132,10 @@ vi.mock('@/editor/ui/Trace', () => ({
|
|
|
127
132
|
clear: vi.fn()
|
|
128
133
|
}
|
|
129
134
|
}));
|
|
135
|
+
vi.mock('@/microbit/MicroBitUpdater', () => ({
|
|
136
|
+
isMicroBitUpdateSupported: microBitUpdaterMocks.isSupported,
|
|
137
|
+
selectAndUpdateMicroBit: microBitUpdaterMocks.selectAndUpdate
|
|
138
|
+
}));
|
|
130
139
|
|
|
131
140
|
describe('Marty connection UI', () => {
|
|
132
141
|
let UI;
|
|
@@ -136,6 +145,7 @@ describe('Marty connection UI', () => {
|
|
|
136
145
|
beforeEach(async () => {
|
|
137
146
|
vi.useFakeTimers();
|
|
138
147
|
vi.clearAllMocks();
|
|
148
|
+
microBitUpdaterMocks.isSupported.mockReturnValue(false);
|
|
139
149
|
global.window = {
|
|
140
150
|
applicationManager: {
|
|
141
151
|
disconnectGeneric: vi.fn(raft => {
|
|
@@ -148,7 +158,8 @@ describe('Marty connection UI', () => {
|
|
|
148
158
|
throw new Error('metadata not ready');
|
|
149
159
|
}),
|
|
150
160
|
removeMarty: vi.fn(),
|
|
151
|
-
setMartySensorAvailability: vi.fn()
|
|
161
|
+
setMartySensorAvailability: vi.fn(),
|
|
162
|
+
getActiveMarty: vi.fn(() => null)
|
|
152
163
|
},
|
|
153
164
|
cogManager: {
|
|
154
165
|
addCog: vi.fn(),
|
|
@@ -158,7 +169,8 @@ describe('Marty connection UI', () => {
|
|
|
158
169
|
microBitManager: {
|
|
159
170
|
addMicroBit: vi.fn(),
|
|
160
171
|
wireMicroBitWithBlocks: vi.fn(),
|
|
161
|
-
removeMicroBit: vi.fn()
|
|
172
|
+
removeMicroBit: vi.fn(),
|
|
173
|
+
getActiveMicroBit: vi.fn(() => null)
|
|
162
174
|
}
|
|
163
175
|
};
|
|
164
176
|
warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
@@ -387,6 +399,70 @@ describe('Marty connection UI', () => {
|
|
|
387
399
|
expect(message).toBe('No micro:bit was selected. Click Connect again when you are ready.');
|
|
388
400
|
});
|
|
389
401
|
|
|
402
|
+
it('opens the software-or-connect dialog for the direct web path when WebUSB is available', () => {
|
|
403
|
+
const button = createConnectionButton();
|
|
404
|
+
const openDialogSpy = vi.spyOn(UI, 'openMicroBitConnectionDialog').mockImplementation(() => {});
|
|
405
|
+
microBitUpdaterMocks.isSupported.mockReturnValue(true);
|
|
406
|
+
|
|
407
|
+
UI.connectMicroBit(button);
|
|
408
|
+
|
|
409
|
+
expect(openDialogSpy).toHaveBeenCalledWith(button);
|
|
410
|
+
openDialogSpy.mockRestore();
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
it('keeps the host-managed micro:bit connection path unchanged', () => {
|
|
414
|
+
const button = createConnectionButton();
|
|
415
|
+
const microBit = createMicroBit();
|
|
416
|
+
const connectGenericMicroBit = vi.fn(callback => callback(microBit));
|
|
417
|
+
const openDialogSpy = vi.spyOn(UI, 'openMicroBitConnectionDialog').mockImplementation(() => {});
|
|
418
|
+
window.applicationManager.connectGenericMicroBit = connectGenericMicroBit;
|
|
419
|
+
microBitUpdaterMocks.isSupported.mockReturnValue(true);
|
|
420
|
+
|
|
421
|
+
UI.connectMicroBit(button);
|
|
422
|
+
|
|
423
|
+
expect(connectGenericMicroBit).toHaveBeenCalledOnce();
|
|
424
|
+
expect(openDialogSpy).not.toHaveBeenCalled();
|
|
425
|
+
expect(microBit.__mbjrHostManaged).toBe(true);
|
|
426
|
+
openDialogSpy.mockRestore();
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
it('shows installation success after the WebUSB updater completes', async () => {
|
|
430
|
+
const dialog = createMicroBitUpdateDialog();
|
|
431
|
+
const renderSpy = vi.spyOn(UI, 'renderMicroBitConnectionDialog').mockImplementation(() => {});
|
|
432
|
+
microBitUpdaterMocks.selectAndUpdate.mockImplementation(async progress => progress(0.755));
|
|
433
|
+
|
|
434
|
+
await UI.startMicroBitUpdate(dialog);
|
|
435
|
+
|
|
436
|
+
expect(renderSpy).toHaveBeenNthCalledWith(1, dialog, 'updating');
|
|
437
|
+
expect(dialog.microBitControls.progress.attributes.value).toBe('75.5');
|
|
438
|
+
expect(dialog.microBitControls.progressLabel.textContent).toBe('Installing 75%');
|
|
439
|
+
expect(renderSpy).toHaveBeenNthCalledWith(2, dialog, 'success');
|
|
440
|
+
renderSpy.mockRestore();
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
it('keeps keyboard focus on a visible installer control as its state changes', () => {
|
|
444
|
+
const dialog = createCompleteMicroBitUpdateDialog();
|
|
445
|
+
|
|
446
|
+
UI.renderMicroBitConnectionDialog(dialog, 'updating');
|
|
447
|
+
expect(dialog.microBitControls.progress.focus).toHaveBeenCalledOnce();
|
|
448
|
+
|
|
449
|
+
UI.renderMicroBitConnectionDialog(dialog, 'success');
|
|
450
|
+
expect(dialog.microBitControls.connectButton.focus).toHaveBeenCalledOnce();
|
|
451
|
+
|
|
452
|
+
UI.renderMicroBitConnectionDialog(dialog, 'error');
|
|
453
|
+
expect(dialog.microBitControls.updateButton.focus).toHaveBeenCalledOnce();
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
it('uses the micro:bit five-character friendly name when it is advertised', () => {
|
|
457
|
+
const microBit = {
|
|
458
|
+
getFriendlyName: () => 'BBC micro:bit [vopet]'
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
expect(UI.getMicroBitFriendlyName(microBit)).toBe('BBC micro:bit [vopet]');
|
|
462
|
+
expect(UI.getMicroBitDisplayName(microBit)).toBe('vopet');
|
|
463
|
+
expect(UI.getMicroBitDisplayName({getFriendlyName: () => 'micro:bit'})).toBe('micro:bit');
|
|
464
|
+
});
|
|
465
|
+
|
|
390
466
|
it('registers micro:bit blocks and restores the button after direct disconnect', () => {
|
|
391
467
|
const button = createConnectionButton();
|
|
392
468
|
const oldOnClick = vi.fn();
|
|
@@ -398,6 +474,9 @@ describe('Marty connection UI', () => {
|
|
|
398
474
|
expect(window.microBitManager.addMicroBit).toHaveBeenCalledWith(microBit);
|
|
399
475
|
expect(window.microBitManager.wireMicroBitWithBlocks).toHaveBeenCalledWith('microbit-1');
|
|
400
476
|
expect(button.classList.contains('connectButtonConnected')).toBe(true);
|
|
477
|
+
expect(button.querySelector('.iconButtonContainer').textContent).toBe('DISCONNECT');
|
|
478
|
+
expect(button.querySelector('.raftNameConnectButton').textContent).toBe('micro:bit One');
|
|
479
|
+
expect(button.attributes.title).toBe('Disconnect micro:bit One');
|
|
401
480
|
|
|
402
481
|
button.onclick();
|
|
403
482
|
vi.advanceTimersByTime(1000);
|
|
@@ -407,8 +486,45 @@ describe('Marty connection UI', () => {
|
|
|
407
486
|
expect(button.onclick).toBe(oldOnClick);
|
|
408
487
|
expect(button.classList.contains('connectButtonConnected')).toBe(false);
|
|
409
488
|
expect(button.querySelector('.iconButtonContainer').classList.contains('notConnectedButtonContainer')).toBe(true);
|
|
489
|
+
expect(button.querySelector('.iconButtonContainer').textContent).toBe('');
|
|
410
490
|
expect(microBit.__disconnectUnsubscribed).toBe(true);
|
|
411
491
|
});
|
|
492
|
+
|
|
493
|
+
it('switches from Marty mode to Sprite mode when micro:bit connects without Marty', () => {
|
|
494
|
+
const button = createConnectionButton();
|
|
495
|
+
const microBit = createMicroBit();
|
|
496
|
+
ScratchJr.isMartyModeEnabled = true;
|
|
497
|
+
|
|
498
|
+
UI.setupMicroBitConnectionButton(button, microBit);
|
|
499
|
+
|
|
500
|
+
expect(ScratchJr.isMartyModeEnabled).toBe(false);
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
it('preserves Marty mode when micro:bit connects alongside Marty', () => {
|
|
504
|
+
const button = createConnectionButton();
|
|
505
|
+
const microBit = createMicroBit();
|
|
506
|
+
const marty = createMartyRaft();
|
|
507
|
+
window.martyManager.getActiveMarty.mockReturnValue(marty);
|
|
508
|
+
ScratchJr.isMartyModeEnabled = true;
|
|
509
|
+
|
|
510
|
+
UI.setupMicroBitConnectionButton(button, microBit);
|
|
511
|
+
|
|
512
|
+
expect(ScratchJr.isMartyModeEnabled).toBe(true);
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
it('switches to Sprite mode when Marty disconnect leaves micro:bit connected', () => {
|
|
516
|
+
const button = createConnectionButton();
|
|
517
|
+
const raft = createMartyRaft();
|
|
518
|
+
const microBit = createMicroBit();
|
|
519
|
+
window.microBitManager.getActiveMicroBit.mockReturnValue(microBit);
|
|
520
|
+
|
|
521
|
+
UI.setupMartyConnectionButton(button, raft);
|
|
522
|
+
expect(ScratchJr.isMartyModeEnabled).toBe(true);
|
|
523
|
+
|
|
524
|
+
button.onclick();
|
|
525
|
+
|
|
526
|
+
expect(ScratchJr.isMartyModeEnabled).toBe(false);
|
|
527
|
+
});
|
|
412
528
|
});
|
|
413
529
|
|
|
414
530
|
function createMartyRaft() {
|
|
@@ -464,6 +580,33 @@ function createMicroBit() {
|
|
|
464
580
|
return microBit;
|
|
465
581
|
}
|
|
466
582
|
|
|
583
|
+
function createMicroBitUpdateDialog() {
|
|
584
|
+
return {
|
|
585
|
+
microBitControls: {
|
|
586
|
+
progress: new FakeElement(),
|
|
587
|
+
progressLabel: new FakeElement()
|
|
588
|
+
}
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
function createCompleteMicroBitUpdateDialog() {
|
|
593
|
+
const dialog = new FakeElement();
|
|
594
|
+
dialog.microBitControls = {
|
|
595
|
+
closeButton: new FakeElement(),
|
|
596
|
+
title: new FakeElement(),
|
|
597
|
+
message: new FakeElement(),
|
|
598
|
+
progressArea: new FakeElement(),
|
|
599
|
+
progress: new FakeElement(),
|
|
600
|
+
progressLabel: new FakeElement(),
|
|
601
|
+
errorDetails: new FakeElement(),
|
|
602
|
+
firmwareHelp: new FakeElement(),
|
|
603
|
+
backButton: new FakeElement(),
|
|
604
|
+
updateButton: new FakeElement(),
|
|
605
|
+
connectButton: new FakeElement()
|
|
606
|
+
};
|
|
607
|
+
return dialog;
|
|
608
|
+
}
|
|
609
|
+
|
|
467
610
|
class FakeElement {
|
|
468
611
|
constructor(classes = []) {
|
|
469
612
|
this.classList = new FakeClassList(classes);
|
|
@@ -474,6 +617,7 @@ class FakeElement {
|
|
|
474
617
|
this.textContent = '';
|
|
475
618
|
this.innerHTML = '';
|
|
476
619
|
this.onclick = null;
|
|
620
|
+
this.focus = vi.fn();
|
|
477
621
|
}
|
|
478
622
|
|
|
479
623
|
querySelector(selector) {
|
|
@@ -504,6 +648,10 @@ class FakeElement {
|
|
|
504
648
|
setAttribute(name, value) {
|
|
505
649
|
this.attributes[name] = value;
|
|
506
650
|
}
|
|
651
|
+
|
|
652
|
+
getAttribute(name) {
|
|
653
|
+
return this.attributes[name];
|
|
654
|
+
}
|
|
507
655
|
}
|
|
508
656
|
|
|
509
657
|
class FakeClassList {
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest';
|
|
2
|
+
|
|
3
|
+
const mocks = vi.hoisted(() => ({
|
|
4
|
+
dapLinks: [],
|
|
5
|
+
flashError: null,
|
|
6
|
+
isUniversalHex: vi.fn(() => true),
|
|
7
|
+
separateUniversalHex: vi.fn(() => [
|
|
8
|
+
{boardId: 0x9900},
|
|
9
|
+
{boardId: 0x9903}
|
|
10
|
+
]),
|
|
11
|
+
webUsbDevices: []
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
vi.mock('@microbit/microbit-universal-hex', () => ({
|
|
15
|
+
isUniversalHex: mocks.isUniversalHex,
|
|
16
|
+
separateUniversalHex: mocks.separateUniversalHex
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
vi.mock('dapjs', () => {
|
|
20
|
+
class WebUSB {
|
|
21
|
+
constructor (device) {
|
|
22
|
+
this.device = device;
|
|
23
|
+
mocks.webUsbDevices.push(device);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
class DAPLink {
|
|
28
|
+
static EVENT_PROGRESS = 'progress';
|
|
29
|
+
|
|
30
|
+
constructor (transport) {
|
|
31
|
+
this.transport = transport;
|
|
32
|
+
this.connected = false;
|
|
33
|
+
this.on = vi.fn();
|
|
34
|
+
this.connect = vi.fn(async () => {
|
|
35
|
+
this.connected = true;
|
|
36
|
+
});
|
|
37
|
+
this.flash = vi.fn(async () => {
|
|
38
|
+
if (mocks.flashError) {
|
|
39
|
+
throw mocks.flashError;
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
this.disconnect = vi.fn(async () => {
|
|
43
|
+
this.connected = false;
|
|
44
|
+
});
|
|
45
|
+
mocks.dapLinks.push(this);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return {DAPLink, WebUSB};
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe('MicroBitUpdater', () => {
|
|
53
|
+
let updater;
|
|
54
|
+
let requestDevice;
|
|
55
|
+
|
|
56
|
+
beforeEach(async () => {
|
|
57
|
+
vi.clearAllMocks();
|
|
58
|
+
mocks.dapLinks.length = 0;
|
|
59
|
+
mocks.flashError = null;
|
|
60
|
+
mocks.webUsbDevices.length = 0;
|
|
61
|
+
mocks.isUniversalHex.mockReturnValue(true);
|
|
62
|
+
mocks.separateUniversalHex.mockReturnValue([
|
|
63
|
+
{boardId: 0x9900},
|
|
64
|
+
{boardId: 0x9903}
|
|
65
|
+
]);
|
|
66
|
+
requestDevice = vi.fn();
|
|
67
|
+
vi.stubGlobal('navigator', {usb: {requestDevice}});
|
|
68
|
+
vi.stubGlobal('fetch', vi.fn(async () => ({
|
|
69
|
+
ok: true,
|
|
70
|
+
text: async () => 'universal micro:bit hex'
|
|
71
|
+
})));
|
|
72
|
+
updater = await import('@/microbit/MicroBitUpdater.js');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
afterEach(() => {
|
|
76
|
+
vi.unstubAllGlobals();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('selects the micro:bit USB interface and flashes the universal HEX', async () => {
|
|
80
|
+
const device = {serialNumber: '9903ABCDEF'};
|
|
81
|
+
const progress = vi.fn();
|
|
82
|
+
requestDevice.mockResolvedValue(device);
|
|
83
|
+
|
|
84
|
+
await updater.selectAndUpdateMicroBit(progress);
|
|
85
|
+
|
|
86
|
+
expect(requestDevice).toHaveBeenCalledWith({
|
|
87
|
+
filters: [{vendorId: 0x0d28, productId: 0x0204}]
|
|
88
|
+
});
|
|
89
|
+
expect(mocks.webUsbDevices).toEqual([device]);
|
|
90
|
+
expect(mocks.dapLinks).toHaveLength(1);
|
|
91
|
+
const dapLink = mocks.dapLinks[0];
|
|
92
|
+
expect(dapLink.on).toHaveBeenCalledWith('progress', progress);
|
|
93
|
+
expect(dapLink.connect).toHaveBeenCalledOnce();
|
|
94
|
+
expect(dapLink.flash).toHaveBeenCalledOnce();
|
|
95
|
+
expect(dapLink.flash.mock.calls[0][0]).toEqual(new TextEncoder().encode('universal micro:bit hex'));
|
|
96
|
+
expect(dapLink.disconnect).toHaveBeenCalledOnce();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('rejects unsupported board identifiers before connecting to DAPLink', async () => {
|
|
100
|
+
requestDevice.mockResolvedValue({serialNumber: '1234ABCDEF'});
|
|
101
|
+
|
|
102
|
+
await expect(updater.selectAndUpdateMicroBit()).rejects.toThrow(
|
|
103
|
+
'Could not identify the micro:bit board version.'
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
expect(mocks.dapLinks[0].connect).not.toHaveBeenCalled();
|
|
107
|
+
expect(mocks.dapLinks[0].flash).not.toHaveBeenCalled();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('reports when the browser does not expose WebUSB', () => {
|
|
111
|
+
vi.stubGlobal('navigator', {});
|
|
112
|
+
|
|
113
|
+
expect(updater.isMicroBitUpdateSupported()).toBe(false);
|
|
114
|
+
return expect(updater.selectAndUpdateMicroBit()).rejects.toThrow(
|
|
115
|
+
'Installing micro:bit software is not supported in this browser.'
|
|
116
|
+
);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('disconnects DAPLink when flashing fails', async () => {
|
|
120
|
+
requestDevice.mockResolvedValue({serialNumber: '9901ABCDEF'});
|
|
121
|
+
mocks.flashError = new Error('flash failed');
|
|
122
|
+
|
|
123
|
+
await expect(updater.selectAndUpdateMicroBit()).rejects.toThrow('flash failed');
|
|
124
|
+
expect(mocks.dapLinks[0].disconnect).toHaveBeenCalledOnce();
|
|
125
|
+
});
|
|
126
|
+
});
|
package/vitest.config.js
CHANGED