@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.
Files changed (52) hide show
  1. package/editions/free/src/30d09ba32a17082ef820.hex +26350 -0
  2. package/editions/free/src/app.bundle.js +1 -1
  3. package/editions/free/src/app.bundle.js.LICENSE.txt +15 -0
  4. package/editions/free/src/assets/blockicons/microbitbuttona.svg +7 -1
  5. package/editions/free/src/assets/blockicons/microbitbuttonab.svg +8 -1
  6. package/editions/free/src/assets/blockicons/microbitbuttonb.svg +7 -1
  7. package/editions/free/src/assets/blockicons/microbitdisplayclear.svg +9 -25
  8. package/editions/free/src/assets/blockicons/microbitdisplayhappy.svg +13 -31
  9. package/editions/free/src/assets/blockicons/microbitdisplayheart.svg +13 -37
  10. package/editions/free/src/assets/blockicons/microbitdisplaytext.svg +10 -25
  11. package/editions/free/src/assets/blockicons/microbittiltleft.svg +1 -1
  12. package/editions/free/src/assets/blockicons/microbittiltright.svg +1 -1
  13. package/editions/free/src/assets/connection/remove_extension-default.svg +5 -0
  14. package/editions/free/src/css/editor.css +171 -5
  15. package/editions/free/src/css/editorleftpanel.css +87 -12
  16. package/editions/free/src/localizations/bg.json +12 -12
  17. package/editions/free/src/localizations/ca.json +12 -12
  18. package/editions/free/src/localizations/cs.json +12 -12
  19. package/editions/free/src/localizations/cy.json +12 -12
  20. package/editions/free/src/localizations/da.json +12 -12
  21. package/editions/free/src/localizations/de.json +12 -12
  22. package/editions/free/src/localizations/el.json +12 -12
  23. package/editions/free/src/localizations/en.json +34 -14
  24. package/editions/free/src/localizations/es.json +12 -12
  25. package/editions/free/src/localizations/fi.json +12 -12
  26. package/editions/free/src/localizations/fr.json +12 -12
  27. package/editions/free/src/localizations/it.json +12 -12
  28. package/editions/free/src/localizations/ja.json +12 -12
  29. package/editions/free/src/localizations/ko.json +12 -12
  30. package/editions/free/src/localizations/nl.json +12 -12
  31. package/editions/free/src/localizations/no.json +12 -12
  32. package/editions/free/src/localizations/pl.json +12 -12
  33. package/editions/free/src/localizations/pt-br.json +12 -12
  34. package/editions/free/src/localizations/pt.json +12 -12
  35. package/editions/free/src/localizations/sv.json +12 -12
  36. package/editions/free/src/localizations/th.json +12 -12
  37. package/editions/free/src/localizations/tr.json +12 -12
  38. package/editions/free/src/localizations/uk.json +12 -12
  39. package/editions/free/src/localizations/zh-cn.json +12 -12
  40. package/editions/free/src/localizations/zh-tw.json +12 -12
  41. package/package.json +4 -1
  42. package/scripts/prepare-microbit-hex.mjs +47 -0
  43. package/tests/BlockGuideRegistry.test.js +4 -2
  44. package/tests/MicroBitBlockDescriptions.test.js +28 -0
  45. package/tests/MicroBitBlockOptions.test.js +34 -0
  46. package/tests/MicroBitButtonIcons.test.js +24 -0
  47. package/tests/MicroBitDisplayIcons.test.js +64 -0
  48. package/tests/e2e/chromium-79-smoke.test.js +126 -10
  49. package/tests/e2e/marty-connection-ui.e2e.test.js +152 -4
  50. package/tests/e2e/microbit-updater.test.js +126 -0
  51. package/vitest.config.js +1 -0
  52. package/webpack.config.js +4 -0
@@ -0,0 +1,47 @@
1
+ import fs from 'node:fs/promises';
2
+ import {createHash} from 'node:crypto';
3
+ import path from 'node:path';
4
+ import {createRequire} from 'node:module';
5
+ import {fileURLToPath} from 'node:url';
6
+
7
+ const require = createRequire(import.meta.url);
8
+ const JSZip = require('jszip');
9
+
10
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
11
+ const projectRoot = path.resolve(__dirname, '..');
12
+ const sourceUrl = 'https://downloads.scratch.mit.edu/microbit/scratch-microbit.hex.zip';
13
+ const outputPath = path.join(projectRoot, 'src', 'assets', 'microbit', 'scratch-microbit.hex');
14
+ const expectedHexSha256 = '6a0cc7c2927ef53c8a8e8e26b8df06778f0a9896af49a98b285f51fff58c53e2';
15
+
16
+ async function prepareMicroBitHex () {
17
+ console.info(`Downloading ${sourceUrl}`);
18
+ const response = await fetch(sourceUrl);
19
+ if (!response.ok) {
20
+ throw new Error(`Could not download the micro:bit HEX archive: HTTP ${response.status}`);
21
+ }
22
+
23
+ const archive = new JSZip(Buffer.from(await response.arrayBuffer()));
24
+ const hexEntryName = Object.keys(archive.files).find(fileName =>
25
+ !archive.files[fileName].dir && /\.hex$/i.test(fileName)
26
+ );
27
+ if (!hexEntryName) {
28
+ throw new Error('The micro:bit HEX archive did not contain a HEX file.');
29
+ }
30
+
31
+ const hexData = archive.file(hexEntryName).asNodeBuffer();
32
+ const actualHexSha256 = createHash('sha256').update(hexData).digest('hex');
33
+ if (actualHexSha256 !== expectedHexSha256) {
34
+ throw new Error(
35
+ `The downloaded micro:bit HEX failed its integrity check. ` +
36
+ `Expected ${expectedHexSha256}, received ${actualHexSha256}.`
37
+ );
38
+ }
39
+ await fs.mkdir(path.dirname(outputPath), {recursive: true});
40
+ await fs.writeFile(outputPath, hexData);
41
+ console.info(`Wrote ${path.relative(projectRoot, outputPath)}`);
42
+ }
43
+
44
+ prepareMicroBitHex().catch(error => {
45
+ console.error(error);
46
+ process.exitCode = 1;
47
+ });
@@ -31,7 +31,7 @@ describe('Blocks Guide registry', () => {
31
31
  const documentedIds = new Set(getDocumentedBlockIds());
32
32
 
33
33
  expect([...documentedIds].sort()).toEqual([...expectedIds].sort());
34
- expect(documentedIds.size).toBe(89);
34
+ expect(documentedIds.size).toBe(88);
35
35
  expect(Object.keys(BLOCK_GUIDE_METADATA).sort()).toEqual([...expectedIds].sort());
36
36
  });
37
37
 
@@ -47,7 +47,7 @@ describe('Blocks Guide registry', () => {
47
47
  expect(BLOCK_GUIDE_MODES[2].categories.map((category) => category.id)).toEqual(
48
48
  ['start', 'looks', 'sound']
49
49
  );
50
- expect(BLOCK_GUIDE_EXTENSION.blockCount).toBe(8);
50
+ expect(BLOCK_GUIDE_EXTENSION.blockCount).toBe(7);
51
51
  });
52
52
 
53
53
  it('provides visible copy and artwork for every documented block', () => {
@@ -56,5 +56,7 @@ describe('Blocks Guide registry', () => {
56
56
  expect(item.description, `${blockId} description`).toBeTruthy();
57
57
  expect(Boolean(item.icon || item.symbol), `${blockId} artwork`).toBe(true);
58
58
  });
59
+ expect(BLOCK_GUIDE_METADATA.microbittilted.icon)
60
+ .toBe('../assets/blockicons/microbittiltright.svg');
59
61
  });
60
62
  });
@@ -0,0 +1,28 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import englishMessages from '../editions/free/src/localizations/en.json';
3
+
4
+ describe('micro:bit block descriptions', () => {
5
+ it('uses concise palette and accessibility labels', () => {
6
+ expect({
7
+ cogButton: englishMessages.BLOCK_DESC_ON_TOUCH_Cog,
8
+ button: englishMessages.BLOCK_DESC_MICROBIT_ON_BUTTON,
9
+ gesture: englishMessages.BLOCK_DESC_MICROBIT_ON_GESTURE,
10
+ tilt: englishMessages.BLOCK_DESC_MICROBIT_ON_TILT,
11
+ heart: englishMessages.BLOCK_DESC_MICROBIT_DISPLAY_HEART,
12
+ happy: englishMessages.BLOCK_DESC_MICROBIT_DISPLAY_HAPPY,
13
+ text: englishMessages.BLOCK_DESC_MICROBIT_DISPLAY_TEXT,
14
+ custom: englishMessages.BLOCK_DESC_MICROBIT_DISPLAY_CUSTOM,
15
+ clear: englishMessages.BLOCK_DESC_MICROBIT_CLEAR_DISPLAY
16
+ }).toEqual({
17
+ cogButton: 'START ON BUTTON PRESS',
18
+ button: 'START ON BUTTON PRESS',
19
+ gesture: 'START ON GESTURE',
20
+ tilt: 'START ON TILT',
21
+ heart: 'DISPLAY HEART',
22
+ happy: 'DISPLAY HAPPY FACE',
23
+ text: 'DISPLAY TEXT',
24
+ custom: 'DISPLAY CUSTOM PATTERN',
25
+ clear: 'CLEAR DISPLAY'
26
+ });
27
+ });
28
+ });
@@ -0,0 +1,34 @@
1
+ import {describe, expect, it} from 'vitest';
2
+ import {
3
+ MICROBIT_DEFAULT_TILT_OPTION,
4
+ MICROBIT_LEGACY_TILT_ANY_OPTION,
5
+ MICROBIT_TILT_OPTIONS,
6
+ resolveMicroBitTiltIcon
7
+ } from '../src/microbit/MicroBitBlockOptions';
8
+
9
+ describe('micro:bit block options', () => {
10
+ it('offers only directional tilt choices for new blocks', () => {
11
+ expect(MICROBIT_TILT_OPTIONS).toEqual([
12
+ 'microbittiltright',
13
+ 'microbittiltleft',
14
+ 'microbittiltbackward',
15
+ 'microbittiltforward'
16
+ ]);
17
+ expect(MICROBIT_DEFAULT_TILT_OPTION).toBe('microbittiltright');
18
+ expect(MICROBIT_TILT_OPTIONS).not.toContain(MICROBIT_LEGACY_TILT_ANY_OPTION);
19
+ });
20
+
21
+ it('keeps the tilt-any artwork for legacy saved arguments', () => {
22
+ expect(resolveMicroBitTiltIcon('microbittiltany', MICROBIT_TILT_OPTIONS))
23
+ .toBe('microbittiltany');
24
+ expect(resolveMicroBitTiltIcon('tilt_any', MICROBIT_TILT_OPTIONS))
25
+ .toBe('microbittiltany');
26
+ expect(resolveMicroBitTiltIcon('tiltright', MICROBIT_TILT_OPTIONS))
27
+ .toBe('microbittiltright');
28
+ });
29
+
30
+ it('falls back safely when an unknown tilt argument is loaded', () => {
31
+ expect(resolveMicroBitTiltIcon('unknown', MICROBIT_TILT_OPTIONS))
32
+ .toBe(MICROBIT_DEFAULT_TILT_OPTION);
33
+ });
34
+ });
@@ -0,0 +1,24 @@
1
+ import {readFileSync} from 'node:fs';
2
+ import {join} from 'node:path';
3
+ import {describe, expect, it} from 'vitest';
4
+
5
+ const ICON_DIRECTORY = join('editions', 'free', 'src', 'assets', 'blockicons');
6
+ const BUTTON_ICONS = [
7
+ {file: 'microbitbuttona.svg', handCount: 1},
8
+ {file: 'microbitbuttonb.svg', handCount: 1},
9
+ {file: 'microbitbuttonab.svg', handCount: 2}
10
+ ];
11
+
12
+ describe('micro:bit button icons', () => {
13
+ BUTTON_ICONS.forEach(({file, handCount}) => {
14
+ it(`${file} uses hands instead of letter labels`, () => {
15
+ const svg = readFileSync(join(ICON_DIRECTORY, file), 'utf8');
16
+ const hands = svg.match(/<use href="#button-press-hand"/g) || [];
17
+
18
+ expect(svg).not.toMatch(/<text\b/);
19
+ expect(svg).toContain('fill="#34332b"');
20
+ expect(svg).toContain('fill="#fff"');
21
+ expect(hands).toHaveLength(handCount);
22
+ });
23
+ });
24
+ });
@@ -0,0 +1,64 @@
1
+ import {readFileSync} from 'node:fs';
2
+ import {join} from 'node:path';
3
+ import {describe, expect, it} from 'vitest';
4
+
5
+ const ASSET_DIRECTORY = join('editions', 'free', 'src', 'assets');
6
+ const DISPLAY_BLOCK_ASSETS = [
7
+ join('blockicons', 'microbitdisplayheart.svg'),
8
+ join('blockicons', 'microbitdisplayhappy.svg'),
9
+ join('blockicons', 'microbitdisplaytext.svg'),
10
+ join('blockicons', 'microbitdisplayclear.svg')
11
+ ];
12
+
13
+ describe('micro:bit display artwork', () => {
14
+ DISPLAY_BLOCK_ASSETS.forEach((file) => {
15
+ it(`${file} uses a display-only treatment`, () => {
16
+ const svg = readFileSync(join(ASSET_DIRECTORY, file), 'utf8');
17
+
18
+ expect(svg).toContain('data-microbit-display="true"');
19
+ expect(svg).toContain('transform="translate(-5 0)"');
20
+ expect(svg).toContain('fill="#414757"');
21
+ expect(svg).not.toMatch(/#35c1df/i);
22
+ expect(svg).not.toMatch(/#4c97ff/i);
23
+ expect(svg).not.toMatch(/#ffbf00/i);
24
+ });
25
+ });
26
+
27
+ DISPLAY_BLOCK_ASSETS.filter((file) => !file.endsWith('microbitdisplayclear.svg')).forEach((file) => {
28
+ it(`${file} uses white for lit LEDs`, () => {
29
+ const svg = readFileSync(join(ASSET_DIRECTORY, file), 'utf8');
30
+
31
+ expect(svg).toContain('#FFFFFF');
32
+ });
33
+ });
34
+
35
+ it('shows clear display as a fully unlit 5x5 matrix without an X', () => {
36
+ const svg = readFileSync(join(ASSET_DIRECTORY, 'blockicons', 'microbitdisplayclear.svg'), 'utf8');
37
+
38
+ expect(svg.match(/rx="0\.8"/g)).toHaveLength(25);
39
+ expect(svg).not.toContain('data-led-state="on"');
40
+ expect(svg).not.toContain('#FFFFFF');
41
+ expect(svg).not.toContain('<path');
42
+ });
43
+
44
+ ['MicroBitLooksOn.svg', 'MicroBitLooksOff.svg'].forEach((file) => {
45
+ it(`${file} keeps the full micro:bit category artwork`, () => {
46
+ const svg = readFileSync(join(ASSET_DIRECTORY, 'categories', file), 'utf8');
47
+
48
+ expect(svg).not.toContain('data-microbit-display="true"');
49
+ expect(svg).toMatch(/#4c97ff/i);
50
+ expect(svg).toMatch(/#ffbf00/i);
51
+ });
52
+ });
53
+
54
+ it('uses the same white LED colour for the custom preview and editor', () => {
55
+ const blockArg = readFileSync(join('src', 'editor', 'blocks', 'BlockArg.js'), 'utf8');
56
+ const editorCss = readFileSync(join('editions', 'free', 'src', 'css', 'editor.css'), 'utf8');
57
+
58
+ expect(blockArg).toContain('drawMicroBitDisplay');
59
+ expect(blockArg).toMatch(/drawMicroBitDisplay\(ctx,.*?, 13, 8, 49, 49\);/);
60
+ expect(blockArg).toContain("enabled ? '#FFFFFF' : '#7C87A5'");
61
+ expect(blockArg).not.toContain('drawMicroBitBoard');
62
+ expect(editorCss).toMatch(/\.microbitMatrixCell\.on\s*\{[^}]*background:\s*#FFFFFF;/s);
63
+ });
64
+ });
@@ -112,6 +112,14 @@ async function openExtensionsLibrary(page) {
112
112
  await page.click("#addExtensionButton");
113
113
  }
114
114
 
115
+ async function getPaletteUndoGap(page) {
116
+ return page.evaluate(() => {
117
+ const palette = document.getElementById("palette").getBoundingClientRect();
118
+ const undoControls = document.getElementById("undocontrols").getBoundingClientRect();
119
+ return undoControls.left - palette.right;
120
+ });
121
+ }
122
+
115
123
  describe("Chromium 79 smoke test", () => {
116
124
  it(
117
125
  "loads home page without console errors",
@@ -127,7 +135,7 @@ describe("Chromium 79 smoke test", () => {
127
135
  );
128
136
 
129
137
  it(
130
- "keeps the extension entry visible and reveals micro:bit controls only while loaded",
138
+ "uses the third connection slot for either the extension entry or micro:bit controls",
131
139
  async () => {
132
140
  const { browser, page, errors } = await openPage("/editor.html?mode=edit");
133
141
 
@@ -137,6 +145,7 @@ describe("Chromium 79 smoke test", () => {
137
145
  const initialState = await page.evaluate(() => {
138
146
  const addButton = document.getElementById("addExtensionButton");
139
147
  const microBitButton = document.getElementById("microBitConnectionButton");
148
+ const removeButton = document.getElementById("microBitRemoveButton");
140
149
  return {
141
150
  addButtonDisplay: window.getComputedStyle(addButton).display,
142
151
  addButtonAriaHidden: addButton.getAttribute("aria-hidden"),
@@ -144,6 +153,9 @@ describe("Chromium 79 smoke test", () => {
144
153
  microBitButtonDisplay: window.getComputedStyle(microBitButton).display,
145
154
  microBitButtonAriaHidden: microBitButton.getAttribute("aria-hidden"),
146
155
  microBitButtonDisabled: microBitButton.disabled,
156
+ removeButtonDisplay: window.getComputedStyle(removeButton).display,
157
+ removeButtonAriaHidden: removeButton.getAttribute("aria-hidden"),
158
+ removeButtonDisabled: removeButton.disabled,
147
159
  rightCategoryIds: Array.from(document.querySelectorAll("#selectorsright .category-selector-button")).map(
148
160
  (button) => button.id
149
161
  ),
@@ -158,6 +170,9 @@ describe("Chromium 79 smoke test", () => {
158
170
  microBitButtonDisplay: "none",
159
171
  microBitButtonAriaHidden: "true",
160
172
  microBitButtonDisabled: true,
173
+ removeButtonDisplay: "none",
174
+ removeButtonAriaHidden: "true",
175
+ removeButtonDisabled: true,
161
176
  rightCategoryIds: ["cog-start", "cog-looks", "cog-sound"],
162
177
  extensionEnabled: false,
163
178
  });
@@ -195,6 +210,8 @@ describe("Chromium 79 smoke test", () => {
195
210
  const enabledState = await page.evaluate(() => {
196
211
  const addButton = document.getElementById("addExtensionButton");
197
212
  const microBitButton = document.getElementById("microBitConnectionButton");
213
+ const removeButton = document.getElementById("microBitRemoveButton");
214
+ const microBitAction = microBitButton.querySelector(".iconButtonContainer");
198
215
  return {
199
216
  addButtonDisplay: window.getComputedStyle(addButton).display,
200
217
  addButtonAriaHidden: addButton.getAttribute("aria-hidden"),
@@ -202,6 +219,13 @@ describe("Chromium 79 smoke test", () => {
202
219
  microBitButtonDisplay: window.getComputedStyle(microBitButton).display,
203
220
  microBitButtonAriaHidden: microBitButton.getAttribute("aria-hidden"),
204
221
  microBitButtonDisabled: microBitButton.disabled,
222
+ removeButtonDisplay: window.getComputedStyle(removeButton).display,
223
+ removeButtonAriaHidden: removeButton.getAttribute("aria-hidden"),
224
+ removeButtonDisabled: removeButton.disabled,
225
+ microBitUsesConnectControl: window.getComputedStyle(microBitAction).backgroundImage.includes(
226
+ "connect_btn-default.svg"
227
+ ),
228
+ microBitActionPosition: window.getComputedStyle(microBitAction).position,
205
229
  rightCategoryIds: Array.from(document.querySelectorAll("#selectorsright .category-selector-button")).map(
206
230
  (button) => button.id
207
231
  ),
@@ -216,22 +240,109 @@ describe("Chromium 79 smoke test", () => {
216
240
  });
217
241
 
218
242
  expect(enabledState).toEqual({
219
- addButtonDisplay: "flex",
220
- addButtonAriaHidden: "false",
221
- addButtonDisabled: false,
243
+ addButtonDisplay: "none",
244
+ addButtonAriaHidden: "true",
245
+ addButtonDisabled: true,
222
246
  microBitButtonDisplay: "flex",
223
247
  microBitButtonAriaHidden: "false",
224
248
  microBitButtonDisabled: false,
249
+ removeButtonDisplay: "flex",
250
+ removeButtonAriaHidden: "false",
251
+ removeButtonDisabled: false,
252
+ microBitUsesConnectControl: true,
253
+ microBitActionPosition: "static",
225
254
  rightCategoryIds: ["cog-start", "cog-looks", "cog-sound", "microbit-start", "microbit-looks"],
226
255
  selectedRightCategoryIds: ["microbit-start"],
227
256
  paletteBlockTypes: [
228
257
  "microbitbuttonpressed",
229
- "microbitgesture",
230
258
  "microbittilted",
231
259
  ],
232
260
  extensionEnabled: true,
233
261
  });
234
262
 
263
+ const microBitTiltState = await page.evaluate(() => {
264
+ const paletteBlock = Array.from(document.querySelectorAll("#palette > div"))
265
+ .find((block) => block.owner && block.owner.blocktype === "microbittilted").owner;
266
+ return {
267
+ defaultValue: paletteBlock.getArgValue(),
268
+ options: JSON.parse(paletteBlock.arg.list),
269
+ };
270
+ });
271
+
272
+ expect(microBitTiltState).toEqual({
273
+ defaultValue: "microbittiltright",
274
+ options: [
275
+ "microbittiltright",
276
+ "microbittiltleft",
277
+ "microbittiltbackward",
278
+ "microbittiltforward",
279
+ ],
280
+ });
281
+
282
+ const legacyMicroBitTiltState = await page.evaluate(() => {
283
+ const scripts = window.ScratchJr.getActiveScript().owner;
284
+ const block = scripts.insertKeyboardBlock(null, [[
285
+ "microbittilted",
286
+ "microbittiltany",
287
+ 50,
288
+ 50,
289
+ ]]);
290
+ return {
291
+ value: block.getArgValue(),
292
+ icon: block.arg.icon,
293
+ options: JSON.parse(block.arg.list),
294
+ };
295
+ });
296
+
297
+ expect(legacyMicroBitTiltState).toEqual({
298
+ value: "microbittiltany",
299
+ icon: "microbittiltany",
300
+ options: [
301
+ "microbittiltright",
302
+ "microbittiltleft",
303
+ "microbittiltbackward",
304
+ "microbittiltforward",
305
+ ],
306
+ });
307
+
308
+ const standardPaletteUndoGap = await getPaletteUndoGap(page);
309
+ const maximumPaletteUndoGap = await page.evaluate(() => window.innerHeight * 0.02);
310
+ await page.setViewport({ width: 1200, height: 600 });
311
+ const widePaletteUndoGap = await getPaletteUndoGap(page);
312
+ await page.setViewport({ width: 800, height: 600 });
313
+
314
+ expect(standardPaletteUndoGap).toBeGreaterThanOrEqual(0);
315
+ expect(standardPaletteUndoGap).toBeLessThanOrEqual(maximumPaletteUndoGap);
316
+ expect(widePaletteUndoGap).toBeCloseTo(standardPaletteUndoGap, 1);
317
+
318
+ const connectedActionState = await page.evaluate(() => {
319
+ const microBitButton = document.getElementById("microBitConnectionButton");
320
+ const microBitAction = microBitButton.querySelector(".iconButtonContainer");
321
+ microBitButton.classList.add("connectButtonConnected");
322
+ microBitAction.classList.remove("notConnectedButtonContainer");
323
+ microBitAction.classList.add("connectedButtonContainer");
324
+ microBitAction.textContent = "DISCONNECT";
325
+
326
+ const actionStyle = window.getComputedStyle(microBitAction);
327
+ const result = {
328
+ text: microBitAction.textContent,
329
+ backgroundImage: actionStyle.backgroundImage,
330
+ position: actionStyle.position,
331
+ };
332
+
333
+ microBitButton.classList.remove("connectButtonConnected");
334
+ microBitAction.classList.remove("connectedButtonContainer");
335
+ microBitAction.classList.add("notConnectedButtonContainer");
336
+ microBitAction.textContent = "";
337
+ return result;
338
+ });
339
+
340
+ expect(connectedActionState).toEqual({
341
+ text: "DISCONNECT",
342
+ backgroundImage: "none",
343
+ position: "static",
344
+ });
345
+
235
346
  await page.click("#microbit-looks");
236
347
  await page.waitForFunction(
237
348
  () => Array.from(document.querySelectorAll("#palette > div")).some(
@@ -285,8 +396,8 @@ describe("Chromium 79 smoke test", () => {
285
396
  });
286
397
  expect(blocksAfterInsert).toContain("microbitdisplayclear");
287
398
 
288
- await openExtensionsLibrary(page);
289
- await page.waitForSelector("#extensionsLibrary.fade.in", { timeout: 30_000 });
399
+ await page.click("#microBitRemoveButton");
400
+ await page.waitForSelector("#microBitExtensionRemoveWarning.show", { timeout: 30_000 });
290
401
  const loadedLibraryState = await page.evaluate(() => ({
291
402
  microBitCardAction: document.getElementById("microBitExtensionCardAction").textContent.trim(),
292
403
  microBitCardLoaded: document.getElementById("microBitExtensionCard").classList.contains("loaded"),
@@ -296,8 +407,6 @@ describe("Chromium 79 smoke test", () => {
296
407
  microBitCardLoaded: true,
297
408
  });
298
409
 
299
- await page.click("#microBitExtensionCard");
300
- await page.waitForSelector("#microBitExtensionRemoveWarning.show", { timeout: 30_000 });
301
410
  await page.click("#microBitExtensionRemoveCancel");
302
411
  await page.waitForFunction(
303
412
  () => !document.getElementById("microBitExtensionRemoveWarning").classList.contains("show"),
@@ -312,7 +421,12 @@ describe("Chromium 79 smoke test", () => {
312
421
  hasMicroBitBlock: true,
313
422
  });
314
423
 
315
- await page.click("#microBitExtensionCard");
424
+ await page.click(".extensionsLibraryClose");
425
+ await page.waitForFunction(
426
+ () => !document.getElementById("extensionsLibrary").classList.contains("in"),
427
+ { timeout: 30_000 }
428
+ );
429
+ await page.click("#microBitRemoveButton");
316
430
  await page.waitForSelector("#microBitExtensionRemoveWarning.show", { timeout: 30_000 });
317
431
  await page.click("#microBitExtensionRemoveConfirm");
318
432
  await page.waitForFunction(
@@ -324,6 +438,7 @@ describe("Chromium 79 smoke test", () => {
324
438
  const unloadedState = await page.evaluate(() => ({
325
439
  addButtonDisplay: window.getComputedStyle(document.getElementById("addExtensionButton")).display,
326
440
  microBitButtonDisplay: window.getComputedStyle(document.getElementById("microBitConnectionButton")).display,
441
+ removeButtonDisplay: window.getComputedStyle(document.getElementById("microBitRemoveButton")).display,
327
442
  extensionsLibraryOpen: document.getElementById("extensionsLibrary").classList.contains("in"),
328
443
  microBitCardAction: document.getElementById("microBitExtensionCardAction").textContent.trim(),
329
444
  microBitCardLoaded: document.getElementById("microBitExtensionCard").classList.contains("loaded"),
@@ -336,6 +451,7 @@ describe("Chromium 79 smoke test", () => {
336
451
  expect(unloadedState).toEqual({
337
452
  addButtonDisplay: "flex",
338
453
  microBitButtonDisplay: "none",
454
+ removeButtonDisplay: "none",
339
455
  extensionsLibraryOpen: false,
340
456
  microBitCardAction: "+",
341
457
  microBitCardLoaded: false,