@robotical/martyblocksjr 4.2.2 → 4.2.4
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/app.bundle.js +1 -1
- package/editions/free/src/css/editor.css +5 -0
- package/editions/free/src/css/editorleftpanel.css +12 -6
- package/editions/free/src/inapp/about.html +55 -26
- package/editions/free/src/inapp/blocks.html +16 -459
- package/editions/free/src/inapp/images/interface.png +0 -0
- package/editions/free/src/inapp/images/paint.png +0 -0
- package/editions/free/src/inapp/interface.html +136 -123
- package/editions/free/src/inapp/style/about.css +333 -21
- package/editions/free/src/inapp/style/blocks.css +423 -82
- package/editions/free/src/inapp/style/interface.css +178 -453
- package/editions/free/src/localizations/bg.json +36 -2
- package/editions/free/src/localizations/ca.json +36 -2
- package/editions/free/src/localizations/cs.json +36 -2
- package/editions/free/src/localizations/cy.json +36 -2
- package/editions/free/src/localizations/da.json +36 -2
- package/editions/free/src/localizations/de.json +36 -2
- package/editions/free/src/localizations/el.json +36 -2
- package/editions/free/src/localizations/en.json +114 -7
- package/editions/free/src/localizations/es.json +36 -2
- package/editions/free/src/localizations/fi.json +36 -2
- package/editions/free/src/localizations/fr.json +36 -2
- package/editions/free/src/localizations/it.json +36 -2
- package/editions/free/src/localizations/ja.json +36 -2
- package/editions/free/src/localizations/ko.json +36 -2
- package/editions/free/src/localizations/nl.json +36 -2
- package/editions/free/src/localizations/no.json +36 -2
- package/editions/free/src/localizations/pl.json +36 -2
- package/editions/free/src/localizations/pt-br.json +36 -2
- package/editions/free/src/localizations/pt.json +36 -2
- package/editions/free/src/localizations/sv.json +36 -2
- package/editions/free/src/localizations/th.json +36 -2
- package/editions/free/src/localizations/tr.json +36 -2
- package/editions/free/src/localizations/uk.json +36 -2
- package/editions/free/src/localizations/zh-cn.json +36 -2
- package/editions/free/src/localizations/zh-tw.json +36 -2
- package/package.json +1 -1
- package/scripts/a11y-structure.mjs +9 -2
- package/tests/BlockGuideRegistry.test.js +60 -0
- package/tests/MartyManager.test.js +88 -0
- package/tests/e2e/accessibility.e2e.test.js +153 -10
- package/tests/e2e/blocks-jr-hidden-blocks.e2e.test.js +84 -0
- package/tests/e2e/chromium-79-smoke.test.js +12 -17
- package/tests/e2e/marty-connection-ui.e2e.test.js +177 -12
- package/tests/e2e/microbit-extension-project-reload.e2e.test.js +1 -6
- package/tests/e2e/project-load-progress-loop.e2e.test.js +1 -6
- package/tests/e2e/sidebar-scrollbars.e2e.test.js +179 -0
|
@@ -0,0 +1,179 @@
|
|
|
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 = 3022;
|
|
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
|
+
}, 30_000);
|
|
34
|
+
|
|
35
|
+
afterAll(() => {
|
|
36
|
+
if (server) {
|
|
37
|
+
server.kill();
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
async function openEditor() {
|
|
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', (error) => errors.push(error.message || String(error)));
|
|
50
|
+
page.on('console', (message) => {
|
|
51
|
+
if (message.type() === 'error') {
|
|
52
|
+
errors.push(message.text());
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
await page.goto(`${HOST}/editor.html?mode=edit`, {
|
|
56
|
+
waitUntil: 'networkidle2',
|
|
57
|
+
timeout: 30_000
|
|
58
|
+
});
|
|
59
|
+
await page.waitForFunction(() => {
|
|
60
|
+
const backdrop = document.getElementById('backdrop');
|
|
61
|
+
return Boolean(
|
|
62
|
+
window.ScratchJr &&
|
|
63
|
+
window.ScratchJr.stage &&
|
|
64
|
+
document.getElementById('spritecc') &&
|
|
65
|
+
document.getElementById('pagecc') &&
|
|
66
|
+
document.getElementById('scrollbar') &&
|
|
67
|
+
document.getElementById('pagescrollbar') &&
|
|
68
|
+
backdrop &&
|
|
69
|
+
window.getComputedStyle(backdrop).display === 'none'
|
|
70
|
+
);
|
|
71
|
+
}, { timeout: 30_000 });
|
|
72
|
+
return { browser, page, errors };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
describe('editor sidebar scrollbars', () => {
|
|
76
|
+
it('shows and synchronizes persistent scrollbars when sprites and pages overflow', async () => {
|
|
77
|
+
const { browser, page, errors } = await openEditor();
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
const naturalSpriteState = await page.evaluate(() => {
|
|
81
|
+
const spriteContainer = document.getElementById('spriteScrollContainer');
|
|
82
|
+
const addSprite = document.getElementById('addsprite');
|
|
83
|
+
return {
|
|
84
|
+
addButtonScrollsWithPanel: addSprite.parentNode === spriteContainer,
|
|
85
|
+
overflow: spriteContainer.scrollHeight - spriteContainer.clientHeight,
|
|
86
|
+
scrollbarVisible: document.getElementById('scrollbar').classList.contains('on')
|
|
87
|
+
};
|
|
88
|
+
});
|
|
89
|
+
expect(naturalSpriteState.addButtonScrollsWithPanel).toBe(true);
|
|
90
|
+
if (naturalSpriteState.overflow <= 1) {
|
|
91
|
+
expect(naturalSpriteState.scrollbarVisible).toBe(false);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
await page.evaluate(() => {
|
|
95
|
+
const spriteSpacer = document.createElement('div');
|
|
96
|
+
spriteSpacer.id = 'sprite-scroll-test-spacer';
|
|
97
|
+
spriteSpacer.style.height = '800px';
|
|
98
|
+
document.getElementById('spritecc').appendChild(spriteSpacer);
|
|
99
|
+
|
|
100
|
+
const pageSpacer = document.createElement('div');
|
|
101
|
+
pageSpacer.id = 'page-scroll-test-spacer';
|
|
102
|
+
pageSpacer.style.height = '800px';
|
|
103
|
+
document.getElementById('pagelist').appendChild(pageSpacer);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
await page.waitForFunction(() =>
|
|
107
|
+
document.getElementById('scrollbar').classList.contains('on') &&
|
|
108
|
+
document.getElementById('pagescrollbar').classList.contains('on')
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
const initialState = await page.evaluate(() => ({
|
|
112
|
+
spriteRole: document.getElementById('scrollbar').getAttribute('role'),
|
|
113
|
+
spriteLabel: document.getElementById('scrollbar').getAttribute('aria-label'),
|
|
114
|
+
spriteControls: document.getElementById('scrollbar').getAttribute('aria-controls'),
|
|
115
|
+
pageRole: document.getElementById('pagescrollbar').getAttribute('role'),
|
|
116
|
+
pageLabel: document.getElementById('pagescrollbar').getAttribute('aria-label'),
|
|
117
|
+
pageControls: document.getElementById('pagescrollbar').getAttribute('aria-controls'),
|
|
118
|
+
spriteThumbHeight: document.getElementById('sbthumb').offsetHeight,
|
|
119
|
+
pageThumbHeight: document.getElementById('pagesbthumb').offsetHeight,
|
|
120
|
+
addSpriteTop: document.getElementById('addsprite').getBoundingClientRect().top
|
|
121
|
+
}));
|
|
122
|
+
expect(initialState).toEqual({
|
|
123
|
+
spriteRole: 'scrollbar',
|
|
124
|
+
spriteLabel: 'Characters',
|
|
125
|
+
spriteControls: 'spriteScrollContainer',
|
|
126
|
+
pageRole: 'scrollbar',
|
|
127
|
+
pageLabel: 'Pages',
|
|
128
|
+
pageControls: 'pagecc',
|
|
129
|
+
spriteThumbHeight: expect.any(Number),
|
|
130
|
+
pageThumbHeight: expect.any(Number),
|
|
131
|
+
addSpriteTop: expect.any(Number)
|
|
132
|
+
});
|
|
133
|
+
expect(initialState.spriteThumbHeight).toBeGreaterThan(0);
|
|
134
|
+
expect(initialState.pageThumbHeight).toBeGreaterThan(0);
|
|
135
|
+
|
|
136
|
+
await page.evaluate(() => {
|
|
137
|
+
const spriteContainer = document.querySelector('#library .spritethumbs');
|
|
138
|
+
const pageContainer = document.getElementById('pagecc');
|
|
139
|
+
spriteContainer.scrollTop = spriteContainer.scrollHeight;
|
|
140
|
+
pageContainer.scrollTop = pageContainer.scrollHeight;
|
|
141
|
+
});
|
|
142
|
+
await page.waitForFunction(() =>
|
|
143
|
+
Number(document.getElementById('scrollbar').getAttribute('aria-valuenow')) > 0 &&
|
|
144
|
+
Number(document.getElementById('pagescrollbar').getAttribute('aria-valuenow')) > 0
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
const scrolledState = await page.evaluate(() => ({
|
|
148
|
+
spriteThumbTop: document.getElementById('sbthumb').offsetTop,
|
|
149
|
+
pageThumbTop: document.getElementById('pagesbthumb').offsetTop,
|
|
150
|
+
spriteValue: Number(document.getElementById('scrollbar').getAttribute('aria-valuenow')),
|
|
151
|
+
pageValue: Number(document.getElementById('pagescrollbar').getAttribute('aria-valuenow')),
|
|
152
|
+
addSpriteTop: document.getElementById('addsprite').getBoundingClientRect().top
|
|
153
|
+
}));
|
|
154
|
+
expect(scrolledState.spriteThumbTop).toBeGreaterThan(0);
|
|
155
|
+
expect(scrolledState.pageThumbTop).toBeGreaterThan(0);
|
|
156
|
+
expect(scrolledState.spriteValue).toBeGreaterThan(0);
|
|
157
|
+
expect(scrolledState.pageValue).toBeGreaterThan(0);
|
|
158
|
+
expect(scrolledState.addSpriteTop).toBeLessThan(initialState.addSpriteTop);
|
|
159
|
+
|
|
160
|
+
await page.evaluate(() => {
|
|
161
|
+
document.getElementById('pagecc').scrollTop = 0;
|
|
162
|
+
});
|
|
163
|
+
const pageScrollBar = await page.$('#pagescrollbar');
|
|
164
|
+
const pageScrollBarBox = await pageScrollBar.boundingBox();
|
|
165
|
+
await page.mouse.click(
|
|
166
|
+
pageScrollBarBox.x + (pageScrollBarBox.width / 2),
|
|
167
|
+
pageScrollBarBox.y + pageScrollBarBox.height - 2
|
|
168
|
+
);
|
|
169
|
+
await page.waitForFunction(() => document.getElementById('pagecc').scrollTop > 0);
|
|
170
|
+
|
|
171
|
+
await page.focus('#pagescrollbar');
|
|
172
|
+
await page.keyboard.press('Home');
|
|
173
|
+
await page.waitForFunction(() => document.getElementById('pagecc').scrollTop === 0);
|
|
174
|
+
expect(errors).toEqual([]);
|
|
175
|
+
} finally {
|
|
176
|
+
await browser.close();
|
|
177
|
+
}
|
|
178
|
+
}, 45_000);
|
|
179
|
+
});
|