@robotical/martyblocksjr 4.1.2 → 4.2.1
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/.firebase/hosting.ZWRpdGlvbnMvZnJlZS9zcmM.cache +599 -534
- package/editions/free/src/app.bundle.js +1 -1
- package/editions/free/src/assets/blockicons/microbitbuttona.svg +33 -0
- package/editions/free/src/assets/blockicons/microbitbuttonab.svg +33 -0
- package/editions/free/src/assets/blockicons/microbitbuttonb.svg +33 -0
- package/editions/free/src/assets/blockicons/microbitdisplayclear.svg +29 -0
- package/editions/free/src/assets/blockicons/microbitdisplayhappy.svg +36 -0
- package/editions/free/src/assets/blockicons/microbitdisplayheart.svg +42 -0
- package/editions/free/src/assets/blockicons/microbitdisplaytext.svg +29 -0
- package/editions/free/src/assets/blockicons/microbitgesturejumped.svg +40 -0
- package/editions/free/src/assets/blockicons/microbitgesturemoved.svg +41 -0
- package/editions/free/src/assets/blockicons/microbitgestureshaken.svg +45 -0
- package/editions/free/src/assets/blockicons/microbittiltany.svg +45 -0
- package/editions/free/src/assets/blockicons/microbittiltbackward.svg +39 -0
- package/editions/free/src/assets/blockicons/microbittiltforward.svg +39 -0
- package/editions/free/src/assets/blockicons/microbittiltleft.svg +39 -0
- package/editions/free/src/assets/blockicons/microbittiltright.svg +39 -0
- package/editions/free/src/assets/categories/MicroBitLooksOff.svg +17 -0
- package/editions/free/src/assets/categories/MicroBitLooksOn.svg +17 -0
- package/editions/free/src/assets/categories/MicroBitStartOff.svg +17 -0
- package/editions/free/src/assets/categories/MicroBitStartOn.svg +17 -0
- package/editions/free/src/assets/ui/viewOnCompact.png +0 -0
- package/editions/free/src/assets/ui/viewOnTargetCompact.png +0 -0
- package/editions/free/src/css/editor.css +334 -1
- package/editions/free/src/css/editorleftpanel.css +86 -26
- package/editions/free/src/css/editorstage.css +62 -0
- package/editions/free/src/css/paintlook.css +23 -0
- package/editions/free/src/localizations/bg.json +10 -1
- package/editions/free/src/localizations/ca.json +10 -1
- package/editions/free/src/localizations/cs.json +10 -1
- package/editions/free/src/localizations/cy.json +10 -1
- package/editions/free/src/localizations/da.json +10 -1
- package/editions/free/src/localizations/de.json +10 -1
- package/editions/free/src/localizations/el.json +10 -1
- package/editions/free/src/localizations/en.json +10 -1
- package/editions/free/src/localizations/es.json +10 -1
- package/editions/free/src/localizations/fi.json +10 -1
- package/editions/free/src/localizations/fr.json +10 -1
- package/editions/free/src/localizations/it.json +10 -1
- package/editions/free/src/localizations/ja.json +10 -1
- package/editions/free/src/localizations/ko.json +10 -1
- package/editions/free/src/localizations/nl.json +10 -1
- package/editions/free/src/localizations/no.json +10 -1
- package/editions/free/src/localizations/pl.json +10 -1
- package/editions/free/src/localizations/pt-br.json +10 -1
- package/editions/free/src/localizations/pt.json +10 -1
- package/editions/free/src/localizations/sv.json +10 -1
- package/editions/free/src/localizations/th.json +10 -1
- package/editions/free/src/localizations/tr.json +10 -1
- package/editions/free/src/localizations/uk.json +10 -1
- package/editions/free/src/localizations/zh-cn.json +10 -1
- package/editions/free/src/localizations/zh-tw.json +10 -1
- package/editions/free/src/media.json +15 -0
- package/editions/free/src/pnglibrary/BeeSprite1.png +0 -0
- package/editions/free/src/svglibrary/BeeSprite1.svg +4 -0
- package/editions/free/src/svglibrary/BeeSprite2.svg +4 -0
- package/editions/free/src/svglibrary/BeeSprite3.svg +4 -0
- package/package.json +1 -1
- package/tests/e2e/accessibility.e2e.test.js +18 -2
- package/tests/e2e/animated-sprite-paint-disabled.e2e.test.js +270 -0
- package/tests/e2e/background-paint-active-selection.e2e.test.js +162 -0
- package/tests/e2e/blocks-jr-hidden-blocks.e2e.test.js +262 -0
- package/tests/e2e/chromium-79-smoke.test.js +235 -0
- package/tests/e2e/delete-last-sprite-mode.e2e.test.js +162 -0
- package/tests/e2e/duplicate-page.e2e.test.js +414 -0
- package/tests/e2e/marty-connection-ui.e2e.test.js +56 -0
- package/tests/e2e/marty-script-activation-paths.e2e.test.js +522 -0
- package/tests/e2e/microbit-blocks.test.js +162 -0
- package/tests/e2e/microbit-extension-project-reload.e2e.test.js +309 -0
- package/tests/e2e/microbit-web-bluetooth.test.js +131 -0
- package/tests/e2e/page-background-loading.test.js +152 -0
- package/tests/e2e/paint-editor-cancel.e2e.test.js +274 -0
- package/tests/e2e/project-load-progress-loop.e2e.test.js +310 -0
- package/tests/e2e/project-save-stall.e2e.test.js +230 -0
- package/tests/e2e/sprite-watermark-grayscale.e2e.test.js +269 -0
- package/tests/e2e/top-toolbar-tooltips.e2e.test.js +152 -0
- package/vitest.config.js +2 -3
|
@@ -108,6 +108,15 @@ async function assertTutorialOpened(page, tutorialId) {
|
|
|
108
108
|
expect(tutorialState.tutorialTitle.length).toBeGreaterThan(0);
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
async function openHiddenExtensionsLibrary(page) {
|
|
112
|
+
await page.$eval("#addExtensionButton", (button) => {
|
|
113
|
+
if (!button.disabled || window.getComputedStyle(button).display !== "none") {
|
|
114
|
+
throw new Error("Expected the extensions entry point to remain hidden and disabled");
|
|
115
|
+
}
|
|
116
|
+
button.onclick();
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
111
120
|
describe("Chromium 79 smoke test", () => {
|
|
112
121
|
it(
|
|
113
122
|
"loads home page without console errors",
|
|
@@ -122,6 +131,232 @@ describe("Chromium 79 smoke test", () => {
|
|
|
122
131
|
60_000
|
|
123
132
|
);
|
|
124
133
|
|
|
134
|
+
it(
|
|
135
|
+
"keeps the extension entry hidden and reveals micro:bit controls only while loaded",
|
|
136
|
+
async () => {
|
|
137
|
+
const { browser, page, errors } = await openPage("/editor.html?mode=edit");
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
await page.waitForSelector("#addExtensionButton", { timeout: 30_000 });
|
|
141
|
+
|
|
142
|
+
const initialState = await page.evaluate(() => {
|
|
143
|
+
const addButton = document.getElementById("addExtensionButton");
|
|
144
|
+
const microBitButton = document.getElementById("microBitConnectionButton");
|
|
145
|
+
return {
|
|
146
|
+
addButtonDisplay: window.getComputedStyle(addButton).display,
|
|
147
|
+
addButtonAriaHidden: addButton.getAttribute("aria-hidden"),
|
|
148
|
+
addButtonDisabled: addButton.disabled,
|
|
149
|
+
microBitButtonDisplay: window.getComputedStyle(microBitButton).display,
|
|
150
|
+
microBitButtonAriaHidden: microBitButton.getAttribute("aria-hidden"),
|
|
151
|
+
microBitButtonDisabled: microBitButton.disabled,
|
|
152
|
+
rightCategoryIds: Array.from(document.querySelectorAll("#selectorsright .category-selector-button")).map(
|
|
153
|
+
(button) => button.id
|
|
154
|
+
),
|
|
155
|
+
extensionEnabled: document.getElementById("connectionButtonsArea").classList.contains("extensionEnabled"),
|
|
156
|
+
};
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
expect(initialState).toEqual({
|
|
160
|
+
addButtonDisplay: "none",
|
|
161
|
+
addButtonAriaHidden: "true",
|
|
162
|
+
addButtonDisabled: true,
|
|
163
|
+
microBitButtonDisplay: "none",
|
|
164
|
+
microBitButtonAriaHidden: "true",
|
|
165
|
+
microBitButtonDisabled: true,
|
|
166
|
+
rightCategoryIds: ["cog-start", "cog-looks", "cog-sound"],
|
|
167
|
+
extensionEnabled: false,
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
await openHiddenExtensionsLibrary(page);
|
|
171
|
+
await page.waitForSelector("#extensionsLibrary.fade.in", { timeout: 30_000 });
|
|
172
|
+
const libraryState = await page.evaluate(() => ({
|
|
173
|
+
title: document.getElementById("extensionsLibraryTitle").textContent.trim(),
|
|
174
|
+
hasMicroBitCard: Boolean(document.getElementById("microBitExtensionCard")),
|
|
175
|
+
microBitCardAction: document.getElementById("microBitExtensionCardAction").textContent.trim(),
|
|
176
|
+
microBitStillHidden:
|
|
177
|
+
window.getComputedStyle(document.getElementById("microBitConnectionButton")).display === "none",
|
|
178
|
+
}));
|
|
179
|
+
|
|
180
|
+
expect(libraryState).toEqual({
|
|
181
|
+
title: "Extensions",
|
|
182
|
+
hasMicroBitCard: true,
|
|
183
|
+
microBitCardAction: "+",
|
|
184
|
+
microBitStillHidden: true,
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
await page.click("#microBitExtensionCard");
|
|
188
|
+
await page.waitForFunction(
|
|
189
|
+
() => {
|
|
190
|
+
const microBitButton = document.getElementById("microBitConnectionButton");
|
|
191
|
+
const library = document.getElementById("extensionsLibrary");
|
|
192
|
+
return microBitButton
|
|
193
|
+
&& window.getComputedStyle(microBitButton).display !== "none"
|
|
194
|
+
&& library
|
|
195
|
+
&& !library.classList.contains("in");
|
|
196
|
+
},
|
|
197
|
+
{ timeout: 30_000 }
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
const enabledState = await page.evaluate(() => {
|
|
201
|
+
const addButton = document.getElementById("addExtensionButton");
|
|
202
|
+
const microBitButton = document.getElementById("microBitConnectionButton");
|
|
203
|
+
return {
|
|
204
|
+
addButtonDisplay: window.getComputedStyle(addButton).display,
|
|
205
|
+
addButtonAriaHidden: addButton.getAttribute("aria-hidden"),
|
|
206
|
+
addButtonDisabled: addButton.disabled,
|
|
207
|
+
microBitButtonDisplay: window.getComputedStyle(microBitButton).display,
|
|
208
|
+
microBitButtonAriaHidden: microBitButton.getAttribute("aria-hidden"),
|
|
209
|
+
microBitButtonDisabled: microBitButton.disabled,
|
|
210
|
+
rightCategoryIds: Array.from(document.querySelectorAll("#selectorsright .category-selector-button")).map(
|
|
211
|
+
(button) => button.id
|
|
212
|
+
),
|
|
213
|
+
selectedRightCategoryIds: Array.from(
|
|
214
|
+
document.querySelectorAll('#selectorsright .category-selector-button[aria-pressed="true"]')
|
|
215
|
+
).map((button) => button.id),
|
|
216
|
+
paletteBlockTypes: Array.from(document.querySelectorAll("#palette > div")).map(
|
|
217
|
+
(block) => block.owner && block.owner.blocktype
|
|
218
|
+
),
|
|
219
|
+
extensionEnabled: document.getElementById("connectionButtonsArea").classList.contains("extensionEnabled"),
|
|
220
|
+
};
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
expect(enabledState).toEqual({
|
|
224
|
+
addButtonDisplay: "none",
|
|
225
|
+
addButtonAriaHidden: "true",
|
|
226
|
+
addButtonDisabled: true,
|
|
227
|
+
microBitButtonDisplay: "flex",
|
|
228
|
+
microBitButtonAriaHidden: "false",
|
|
229
|
+
microBitButtonDisabled: false,
|
|
230
|
+
rightCategoryIds: ["cog-start", "cog-looks", "cog-sound", "microbit-start", "microbit-looks"],
|
|
231
|
+
selectedRightCategoryIds: ["microbit-start"],
|
|
232
|
+
paletteBlockTypes: [
|
|
233
|
+
"microbitbuttonpressed",
|
|
234
|
+
"microbitgesture",
|
|
235
|
+
"microbittilted",
|
|
236
|
+
],
|
|
237
|
+
extensionEnabled: true,
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
await page.click("#microbit-looks");
|
|
241
|
+
await page.waitForFunction(
|
|
242
|
+
() => Array.from(document.querySelectorAll("#palette > div")).some(
|
|
243
|
+
(block) => block.owner && block.owner.blocktype === "microbitdisplaycustom"
|
|
244
|
+
),
|
|
245
|
+
{ timeout: 30_000 }
|
|
246
|
+
);
|
|
247
|
+
const looksPaletteBlockTypes = await page.evaluate(() =>
|
|
248
|
+
Array.from(document.querySelectorAll("#palette > div")).map((block) => block.owner && block.owner.blocktype)
|
|
249
|
+
);
|
|
250
|
+
expect(looksPaletteBlockTypes).toEqual([
|
|
251
|
+
"microbitdisplayheart",
|
|
252
|
+
"microbitdisplayhappy",
|
|
253
|
+
"microbitdisplaycustom",
|
|
254
|
+
"microbitdisplaytext",
|
|
255
|
+
"microbitdisplayclear",
|
|
256
|
+
]);
|
|
257
|
+
|
|
258
|
+
const customMatrixState = await page.evaluate(() => {
|
|
259
|
+
const scripts = window.ScratchJr.getActiveScript().owner;
|
|
260
|
+
const block = scripts.insertKeyboardBlock(null, [[
|
|
261
|
+
"microbitdisplaycustom",
|
|
262
|
+
"00000/00000/00000/00000/00000",
|
|
263
|
+
50,
|
|
264
|
+
50,
|
|
265
|
+
]]);
|
|
266
|
+
block.arg.pressMicroBitMatrixEditor({
|
|
267
|
+
preventDefault() {},
|
|
268
|
+
stopPropagation() {},
|
|
269
|
+
});
|
|
270
|
+
const cells = Array.from(document.querySelectorAll(".microbitMatrixCell"));
|
|
271
|
+
cells[12].dispatchEvent(new PointerEvent("pointerdown", { bubbles: true, cancelable: true }));
|
|
272
|
+
return {
|
|
273
|
+
argValue: block.getArgValue(),
|
|
274
|
+
cellCount: cells.length,
|
|
275
|
+
menuOpen: Boolean(document.querySelector(".microbitMatrixMenu")),
|
|
276
|
+
activeCells: Array.from(document.querySelectorAll(".microbitMatrixCell.on")).length,
|
|
277
|
+
};
|
|
278
|
+
});
|
|
279
|
+
expect(customMatrixState).toEqual({
|
|
280
|
+
argValue: "00000/00000/00100/00000/00000",
|
|
281
|
+
cellCount: 25,
|
|
282
|
+
menuOpen: true,
|
|
283
|
+
activeCells: 1,
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
const blocksAfterInsert = await page.evaluate(() => {
|
|
287
|
+
const scripts = window.ScratchJr.getActiveScript().owner;
|
|
288
|
+
scripts.insertKeyboardBlock(null, [["microbitdisplayclear", "null", 20, 20]]);
|
|
289
|
+
return window.ScratchJr.getBlocks().map((block) => block.blocktype);
|
|
290
|
+
});
|
|
291
|
+
expect(blocksAfterInsert).toContain("microbitdisplayclear");
|
|
292
|
+
|
|
293
|
+
await openHiddenExtensionsLibrary(page);
|
|
294
|
+
await page.waitForSelector("#extensionsLibrary.fade.in", { timeout: 30_000 });
|
|
295
|
+
const loadedLibraryState = await page.evaluate(() => ({
|
|
296
|
+
microBitCardAction: document.getElementById("microBitExtensionCardAction").textContent.trim(),
|
|
297
|
+
microBitCardLoaded: document.getElementById("microBitExtensionCard").classList.contains("loaded"),
|
|
298
|
+
}));
|
|
299
|
+
expect(loadedLibraryState).toEqual({
|
|
300
|
+
microBitCardAction: "-",
|
|
301
|
+
microBitCardLoaded: true,
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
await page.click("#microBitExtensionCard");
|
|
305
|
+
await page.waitForSelector("#microBitExtensionRemoveWarning.show", { timeout: 30_000 });
|
|
306
|
+
await page.click("#microBitExtensionRemoveCancel");
|
|
307
|
+
await page.waitForFunction(
|
|
308
|
+
() => !document.getElementById("microBitExtensionRemoveWarning").classList.contains("show"),
|
|
309
|
+
{ timeout: 30_000 }
|
|
310
|
+
);
|
|
311
|
+
const cancelUnloadState = await page.evaluate(() => ({
|
|
312
|
+
microBitButtonDisplay: window.getComputedStyle(document.getElementById("microBitConnectionButton")).display,
|
|
313
|
+
hasMicroBitBlock: window.ScratchJr.getBlocks().some((block) => block.blocktype === "microbitdisplayclear"),
|
|
314
|
+
}));
|
|
315
|
+
expect(cancelUnloadState).toEqual({
|
|
316
|
+
microBitButtonDisplay: "flex",
|
|
317
|
+
hasMicroBitBlock: true,
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
await page.click("#microBitExtensionCard");
|
|
321
|
+
await page.waitForSelector("#microBitExtensionRemoveWarning.show", { timeout: 30_000 });
|
|
322
|
+
await page.click("#microBitExtensionRemoveConfirm");
|
|
323
|
+
await page.waitForFunction(
|
|
324
|
+
() => window.getComputedStyle(document.getElementById("microBitConnectionButton")).display === "none"
|
|
325
|
+
&& !document.querySelector("#selectorsright #microbit-start")
|
|
326
|
+
&& !document.getElementById("extensionsLibrary").classList.contains("in"),
|
|
327
|
+
{ timeout: 30_000 }
|
|
328
|
+
);
|
|
329
|
+
const unloadedState = await page.evaluate(() => ({
|
|
330
|
+
addButtonDisplay: window.getComputedStyle(document.getElementById("addExtensionButton")).display,
|
|
331
|
+
microBitButtonDisplay: window.getComputedStyle(document.getElementById("microBitConnectionButton")).display,
|
|
332
|
+
extensionsLibraryOpen: document.getElementById("extensionsLibrary").classList.contains("in"),
|
|
333
|
+
microBitCardAction: document.getElementById("microBitExtensionCardAction").textContent.trim(),
|
|
334
|
+
microBitCardLoaded: document.getElementById("microBitExtensionCard").classList.contains("loaded"),
|
|
335
|
+
rightCategoryIds: Array.from(document.querySelectorAll("#selectorsright .category-selector-button")).map(
|
|
336
|
+
(button) => button.id
|
|
337
|
+
),
|
|
338
|
+
hasMicroBitBlock: window.ScratchJr.getBlocks().some((block) => block.blocktype.indexOf("microbit") === 0),
|
|
339
|
+
extensionEnabled: document.getElementById("connectionButtonsArea").classList.contains("extensionEnabled"),
|
|
340
|
+
}));
|
|
341
|
+
expect(unloadedState).toEqual({
|
|
342
|
+
addButtonDisplay: "none",
|
|
343
|
+
microBitButtonDisplay: "none",
|
|
344
|
+
extensionsLibraryOpen: false,
|
|
345
|
+
microBitCardAction: "+",
|
|
346
|
+
microBitCardLoaded: false,
|
|
347
|
+
rightCategoryIds: ["cog-start", "cog-looks", "cog-sound"],
|
|
348
|
+
hasMicroBitBlock: false,
|
|
349
|
+
extensionEnabled: false,
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
expect(errors).toEqual([]);
|
|
353
|
+
} finally {
|
|
354
|
+
await browser.close();
|
|
355
|
+
}
|
|
356
|
+
},
|
|
357
|
+
60_000
|
|
358
|
+
);
|
|
359
|
+
|
|
125
360
|
it(
|
|
126
361
|
"opens tutorials from the book tab and returns there from tutorial mode",
|
|
127
362
|
async () => {
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
2
|
+
import puppeteer from "puppeteer";
|
|
3
|
+
import { spawn } from "child_process";
|
|
4
|
+
import http from "http";
|
|
5
|
+
|
|
6
|
+
const PORT = 3015;
|
|
7
|
+
const HOST = `http://localhost:${PORT}`;
|
|
8
|
+
|
|
9
|
+
let server;
|
|
10
|
+
|
|
11
|
+
async function waitForServer(maxAttempts = 20) {
|
|
12
|
+
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
|
13
|
+
const ok = await new Promise((resolve) => {
|
|
14
|
+
const req = http.get(`${HOST}/`, (res) => {
|
|
15
|
+
res.destroy();
|
|
16
|
+
resolve(true);
|
|
17
|
+
});
|
|
18
|
+
req.on("error", () => resolve(false));
|
|
19
|
+
});
|
|
20
|
+
if (ok) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
24
|
+
}
|
|
25
|
+
throw new Error("Local server did not start in time");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
beforeAll(async () => {
|
|
29
|
+
server = spawn("python3", ["-m", "http.server", `${PORT}`, "--directory", "editions/free/src"], {
|
|
30
|
+
stdio: "ignore",
|
|
31
|
+
});
|
|
32
|
+
await waitForServer();
|
|
33
|
+
}, 30000);
|
|
34
|
+
|
|
35
|
+
afterAll(() => {
|
|
36
|
+
if (server) {
|
|
37
|
+
server.kill();
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
async function openPage(pathname) {
|
|
42
|
+
const browser = await puppeteer.launch({
|
|
43
|
+
headless: true,
|
|
44
|
+
args: ["--no-sandbox"],
|
|
45
|
+
});
|
|
46
|
+
const page = await browser.newPage();
|
|
47
|
+
const errors = [];
|
|
48
|
+
|
|
49
|
+
page.on("pageerror", (err) => errors.push(err.message || String(err)));
|
|
50
|
+
page.on("console", (msg) => {
|
|
51
|
+
if (msg.type() === "error") {
|
|
52
|
+
errors.push(msg.text());
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
await page.goto(`${HOST}${pathname}`, { waitUntil: "networkidle2", timeout: 30000 });
|
|
57
|
+
|
|
58
|
+
return { browser, page, errors };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function waitForEditorReady(page) {
|
|
62
|
+
await page.waitForFunction(
|
|
63
|
+
() => {
|
|
64
|
+
const backdrop = document.getElementById("backdrop");
|
|
65
|
+
const selectedSpriteThumb = document.querySelector("#spritecc .spritethumb[aria-pressed=\"true\"]");
|
|
66
|
+
return Boolean(
|
|
67
|
+
window.ScratchJr
|
|
68
|
+
&& window.ScratchJr.stage
|
|
69
|
+
&& window.ScratchJr.stage.currentPage
|
|
70
|
+
&& document.getElementById("addsprite")
|
|
71
|
+
&& document.getElementById("martyMode")
|
|
72
|
+
&& selectedSpriteThumb
|
|
73
|
+
&& backdrop
|
|
74
|
+
&& window.getComputedStyle(backdrop).display === "none"
|
|
75
|
+
&& !window.ScratchJr.onHold
|
|
76
|
+
&& window.ScratchJr.getActiveScript()
|
|
77
|
+
);
|
|
78
|
+
},
|
|
79
|
+
{ timeout: 30000 }
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function getEditorState(page) {
|
|
84
|
+
return page.evaluate(() => {
|
|
85
|
+
const currentPage = window.ScratchJr.stage.currentPage;
|
|
86
|
+
const spriteIds = currentPage.getSprites();
|
|
87
|
+
const normalSpriteIds = spriteIds.filter(
|
|
88
|
+
(spriteId) => spriteId.indexOf(window.ScratchJr.BIRDS_EYE_SPRITE_NAME) === -1
|
|
89
|
+
);
|
|
90
|
+
const currentSpriteName = currentPage.currentSpriteName || "";
|
|
91
|
+
const addSpriteButton = document.getElementById("addsprite");
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
currentSpriteName,
|
|
95
|
+
spriteIds,
|
|
96
|
+
normalSpriteIds,
|
|
97
|
+
isMartyModeEnabled: window.ScratchJr.isMartyModeEnabled,
|
|
98
|
+
activeSpriteIsMartyBirdsEye:
|
|
99
|
+
currentSpriteName.indexOf(window.ScratchJr.BIRDS_EYE_SPRITE_NAME) > -1,
|
|
100
|
+
hasMartyModeSidebarCard: Boolean(document.getElementById("martyModeSidebarCard")),
|
|
101
|
+
addSpriteButtonVisible:
|
|
102
|
+
Boolean(addSpriteButton) && window.getComputedStyle(addSpriteButton).display !== "none",
|
|
103
|
+
addSpriteButtonDisabled: Boolean(addSpriteButton && addSpriteButton.disabled),
|
|
104
|
+
};
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function deleteSelectedSpriteWithKeyboard(page) {
|
|
109
|
+
await page.focus("#spritecc .spritethumb[aria-pressed=\"true\"]");
|
|
110
|
+
await page.keyboard.press("Delete");
|
|
111
|
+
await page.waitForFunction(
|
|
112
|
+
() => {
|
|
113
|
+
const currentPage = window.ScratchJr.stage.currentPage;
|
|
114
|
+
return currentPage.getSprites().filter(
|
|
115
|
+
(spriteId) => spriteId.indexOf(window.ScratchJr.BIRDS_EYE_SPRITE_NAME) === -1
|
|
116
|
+
).length === 0;
|
|
117
|
+
},
|
|
118
|
+
{ timeout: 30000 }
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
describe("deleting the final sprite", () => {
|
|
123
|
+
it(
|
|
124
|
+
"keeps the editor in Sprite mode when no normal sprites remain",
|
|
125
|
+
async () => {
|
|
126
|
+
const { browser, page, errors } = await openPage("/editor.html?mode=edit");
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
await waitForEditorReady(page);
|
|
130
|
+
|
|
131
|
+
const beforeDelete = await getEditorState(page);
|
|
132
|
+
expect(beforeDelete).toMatchObject({
|
|
133
|
+
normalSpriteIds: [beforeDelete.currentSpriteName],
|
|
134
|
+
isMartyModeEnabled: false,
|
|
135
|
+
activeSpriteIsMartyBirdsEye: false,
|
|
136
|
+
hasMartyModeSidebarCard: false,
|
|
137
|
+
addSpriteButtonVisible: true,
|
|
138
|
+
addSpriteButtonDisabled: false,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
await deleteSelectedSpriteWithKeyboard(page);
|
|
142
|
+
|
|
143
|
+
const afterDelete = await getEditorState(page);
|
|
144
|
+
expect(
|
|
145
|
+
afterDelete,
|
|
146
|
+
`Editor state after deleting the last normal sprite: ${JSON.stringify(afterDelete, null, 2)}`
|
|
147
|
+
).toMatchObject({
|
|
148
|
+
normalSpriteIds: [],
|
|
149
|
+
isMartyModeEnabled: false,
|
|
150
|
+
activeSpriteIsMartyBirdsEye: false,
|
|
151
|
+
hasMartyModeSidebarCard: false,
|
|
152
|
+
addSpriteButtonVisible: true,
|
|
153
|
+
addSpriteButtonDisabled: false,
|
|
154
|
+
});
|
|
155
|
+
expect(errors).toEqual([]);
|
|
156
|
+
} finally {
|
|
157
|
+
await browser.close();
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
90000
|
|
161
|
+
);
|
|
162
|
+
});
|