diff --git a/package.json b/package.json index 645ee73e444b378561925b99898cd7313d5d7f29..3ed372dc9300a8f3793aeea43004b970897fe56a 100644 --- a/package.json +++ b/package.json @@ -42,11 +42,11 @@ "copy-webpack-plugin": "^4.0.1", "css-loader": "^0.28.7", "enzyme": "^3.1.0", - "enzyme-adapter-react-16": "1.0.4", + "enzyme-adapter-react-16": "1.1.0", "eslint": "^4.7.1", "eslint-config-scratch": "^5.0.0", "eslint-plugin-import": "^2.7.0", - "eslint-plugin-react": "^7.2.1", + "eslint-plugin-react": "^7.5.1", "file-loader": "1.1.5", "get-float-time-domain-data": "0.1.0", "gh-pages": "github:rschamp/gh-pages#publish-branch-to-subfolder", @@ -67,9 +67,9 @@ "prop-types": "^15.5.10", "raf": "^3.4.0", "raw-loader": "0.5.1", - "react": "16.1.0", + "react": "16.1.1", "react-contextmenu": "2.8.0", - "react-dom": "16.1.0", + "react-dom": "16.1.1", "react-draggable": "3.0.3", "react-intl": "2.4.0", "react-intl-redux": "0.7.0", @@ -77,7 +77,7 @@ "react-redux": "5.0.6", "react-responsive": "3.0.0", "react-style-proptype": "3.1.0", - "react-tabs": "2.1.0", + "react-tabs": "2.1.1", "react-test-renderer": "16.1.1", "redux": "3.7.0", "redux-mock-store": "^1.2.3", diff --git a/src/components/asset-panel/selector.css b/src/components/asset-panel/selector.css index 2a4de1a252be6c1f4e70893f462493fc8fd18886..63e6868326617b20c9ae347b20a71f29400c195c 100644 --- a/src/components/asset-panel/selector.css +++ b/src/components/asset-panel/selector.css @@ -1,4 +1,5 @@ @import "../../css/colors.css"; +@import "../../css/units.css"; .wrapper { width: 150px; @@ -51,3 +52,14 @@ min-height: 5rem; margin: 1rem auto; } + +@media only screen and (max-width: $full-size-paint) { + .wrapper { + width: 80px; + } + + .list-item { + width: 4rem; + min-height: 4rem; + } +} diff --git a/src/components/close-button/close-button.jsx b/src/components/close-button/close-button.jsx index 416adec8e279b7c185468b376e10fb46bf729416..e90bd1d64b892306ecb613d15e6c2d891fc9d576 100644 --- a/src/components/close-button/close-button.jsx +++ b/src/components/close-button/close-button.jsx @@ -7,6 +7,7 @@ import closeIcon from './icon--close.svg'; const CloseButton = props => ( <div + aria-label="Close" className={classNames( styles.closeButton, props.className, @@ -15,6 +16,7 @@ const CloseButton = props => ( [styles.large]: props.size === CloseButton.SIZE_LARGE } )} + role="button" onClick={props.onClick} > <img diff --git a/src/components/icon-button/icon-button.jsx b/src/components/icon-button/icon-button.jsx index 50d5557e508110531cb045556bb7ff7323eb02fd..c6f9ae3d03ccf48df55b83c7c58295354d3427dd 100644 --- a/src/components/icon-button/icon-button.jsx +++ b/src/components/icon-button/icon-button.jsx @@ -11,6 +11,7 @@ const IconButton = ({ }) => ( <div className={classNames(styles.container, className)} + role="button" onClick={onClick} > <img diff --git a/src/components/language-selector/language-selector.css b/src/components/language-selector/language-selector.css index 5e1a41aee1da7877f00d45d6f31487f91fdaa78f..a060131a8fc2d2c8aa6afa58629622e3a141320f 100644 --- a/src/components/language-selector/language-selector.css +++ b/src/components/language-selector/language-selector.css @@ -22,3 +22,7 @@ color: #3373cc; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; } + +.language-select:focus { + border: 1px solid white; +} diff --git a/src/components/sprite-info/sprite-info.css b/src/components/sprite-info/sprite-info.css index 200aedaf79f13627abf74a4c9378ccb0f60eb300..f018abe8e604bc3652de5431731b18ed42110998 100644 --- a/src/components/sprite-info/sprite-info.css +++ b/src/components/sprite-info/sprite-info.css @@ -62,6 +62,11 @@ border-bottom-left-radius: $form-radius; } +.radio-left:focus { + border-color: #4c97ff; + box-shadow: inset 0 0 0 -2px rgba(0, 0, 0, 0.1); +} + .radio-right { border-bottom: 1px solid $form-border; border-top: 1px solid $form-border; @@ -70,6 +75,11 @@ border-bottom-right-radius: $form-radius; } +.radio-right:focus { + border-color: #4c97ff; + box-shadow: inset 0 0 0 -2px rgba(0, 0, 0, 0.1); +} + .icon { width: 100%; height: 100%; diff --git a/src/components/sprite-info/sprite-info.jsx b/src/components/sprite-info/sprite-info.jsx index f9bbb64e32748af1f955db724d8dc113caea8648..9e1c8a4098d2aeae08e55cf9aa1d95ccedb5bbd9 100644 --- a/src/components/sprite-info/sprite-info.jsx +++ b/src/components/sprite-info/sprite-info.jsx @@ -54,6 +54,7 @@ class SpriteInfo extends React.Component { <MediaQuery minWidth={layout.fullSizeMinWidth}> <div className={styles.iconWrapper}> <img + aria-hidden="true" className={classNames(styles.xIcon, styles.icon)} src={xIcon} /> @@ -76,6 +77,7 @@ class SpriteInfo extends React.Component { <MediaQuery minWidth={layout.fullSizeMinWidth}> <div className={styles.iconWrapper}> <img + aria-hidden="true" className={classNames(styles.yIcon, styles.icon)} src={yIcon} /> @@ -116,6 +118,7 @@ class SpriteInfo extends React.Component { )} tabIndex="4" onClick={this.props.onClickVisible} + onKeyPress={this.props.onPressVisible} > <img className={styles.icon} @@ -132,8 +135,9 @@ class SpriteInfo extends React.Component { [styles.isDisabled]: this.props.disabled } )} - tabIndex="4" + tabIndex="5" onClick={this.props.onClickNotVisible} + onKeyPress={this.props.onPressNotVisible} > <img className={styles.icon} @@ -151,7 +155,7 @@ class SpriteInfo extends React.Component { small disabled={this.props.disabled} label="Direction" - tabIndex="5" + tabIndex="6" type="text" value={this.props.disabled ? '' : this.props.direction} onSubmit={this.props.onChangeDirection} @@ -200,6 +204,8 @@ SpriteInfo.propTypes = { onChangeY: PropTypes.func, onClickNotVisible: PropTypes.func, onClickVisible: PropTypes.func, + onPressNotVisible: PropTypes.func, + onPressVisible: PropTypes.func, rotationStyle: PropTypes.oneOf(ROTATION_STYLES), visible: PropTypes.bool, x: PropTypes.oneOfType([ diff --git a/src/containers/blocks.jsx b/src/containers/blocks.jsx index 471dc6cd08643341c290f66227b723ef28dbffb1..f3e46dfaf2bd9f88496cbaed2a29e9ce1ae62061 100644 --- a/src/containers/blocks.jsx +++ b/src/containers/blocks.jsx @@ -172,6 +172,11 @@ class Blocks extends React.Component { this.workspace.reportValue(data.id, data.value); } onWorkspaceUpdate (data) { + // When we change sprites, update the toolbox to have the new sprite's blocks + if (this.props.vm.editingTarget) { + this.props.updateToolboxState(makeToolboxXML(this.props.vm.editingTarget.id)); + } + if (this.props.vm.editingTarget && !this.state.workspaceMetrics[this.props.vm.editingTarget.id]) { this.onWorkspaceMetricsChange(); } @@ -179,6 +184,8 @@ class Blocks extends React.Component { // Remove and reattach the workspace listener (but allow flyout events) this.workspace.removeChangeListener(this.props.vm.blockListener); const dom = this.ScratchBlocks.Xml.textToDom(data.xml); + // @todo This line rerenders toolbox, and the change in the toolbox XML also rerenders the toolbox. + // We should only rerender the toolbox once. See https://github.com/LLK/scratch-gui/issues/901 this.ScratchBlocks.Xml.clearWorkspaceAndLoadFromXml(dom, this.workspace); this.workspace.addChangeListener(this.props.vm.blockListener); @@ -193,8 +200,8 @@ class Blocks extends React.Component { handleExtensionAdded (blocksInfo) { this.ScratchBlocks.defineBlocksWithJsonArray(blocksInfo.map(blockInfo => blockInfo.json)); const dynamicBlocksXML = this.props.vm.runtime.getBlocksXML(); - const toolboxXML = makeToolboxXML(dynamicBlocksXML); - this.props.onExtensionAdded(toolboxXML); + const toolboxXML = makeToolboxXML(this.props.vm.editingTarget.id, dynamicBlocksXML); + this.props.updateToolboxState(toolboxXML); } handleCategorySelected (categoryName) { this.workspace.toolbox_.setSelectedCategoryByName(categoryName); @@ -220,7 +227,7 @@ class Blocks extends React.Component { vm, isVisible, onActivateColorPicker, - onExtensionAdded, + updateToolboxState, onRequestCloseExtensionLibrary, toolboxXML, ...props @@ -257,7 +264,6 @@ Blocks.propTypes = { extensionLibraryVisible: PropTypes.bool, isVisible: PropTypes.bool, onActivateColorPicker: PropTypes.func, - onExtensionAdded: PropTypes.func, onRequestCloseExtensionLibrary: PropTypes.func, options: PropTypes.shape({ media: PropTypes.string, @@ -281,6 +287,7 @@ Blocks.propTypes = { comments: PropTypes.bool }), toolboxXML: PropTypes.string, + updateToolboxState: PropTypes.func, vm: PropTypes.instanceOf(VM).isRequired }; @@ -322,11 +329,11 @@ const mapStateToProps = state => ({ const mapDispatchToProps = dispatch => ({ onActivateColorPicker: callback => dispatch(activateColorPicker(callback)), - onExtensionAdded: toolboxXML => { - dispatch(updateToolbox(toolboxXML)); - }, onRequestCloseExtensionLibrary: () => { dispatch(closeExtensionLibrary()); + }, + updateToolboxState: toolboxXML => { + dispatch(updateToolbox(toolboxXML)); } }); diff --git a/src/containers/sound-tab.jsx b/src/containers/sound-tab.jsx index 1984923d541427b16a82980d1c6bc627f4541413..6143175ddace4a3755d81c743bc8c81192cd23ad 100644 --- a/src/containers/sound-tab.jsx +++ b/src/containers/sound-tab.jsx @@ -52,20 +52,18 @@ class SoundTab extends React.Component { render () { const { - editingTarget, - sprites, - stage, + vm, onNewSoundFromLibraryClick, onNewSoundFromRecordingClick } = this.props; - const target = editingTarget && sprites[editingTarget] ? sprites[editingTarget] : stage; - - if (!target) { + if (!vm.editingTarget) { return null; } - const sounds = target.sounds ? target.sounds.map(sound => ( + const sprite = vm.editingTarget.sprite; + + const sounds = sprite.sounds ? sprite.sounds.map(sound => ( { url: soundIcon, name: sound.name @@ -106,7 +104,7 @@ class SoundTab extends React.Component { onDeleteClick={this.handleDeleteSound} onItemClick={this.handleSelectSound} > - {editingTarget && target.sounds && target.sounds.length > 0 ? ( + {sprite.sounds && sprite.sounds.length > 0 ? ( <SoundEditor soundIndex={this.state.selectedSoundIndex} /> ) : null} {this.props.soundRecorderVisible ? ( diff --git a/src/containers/sprite-info.jsx b/src/containers/sprite-info.jsx index 532fb37e5fdb8c9af94602ef825257b7bb1d64a9..f4b5ca696d2118898bdbd73c87b30173f8acf52d 100644 --- a/src/containers/sprite-info.jsx +++ b/src/containers/sprite-info.jsx @@ -10,7 +10,9 @@ class SpriteInfo extends React.Component { bindAll(this, [ 'handleChangeRotationStyle', 'handleClickVisible', - 'handleClickNotVisible' + 'handleClickNotVisible', + 'handlePressVisible', + 'handlePressNotVisible' ]); } handleChangeRotationStyle (e) { @@ -24,6 +26,18 @@ class SpriteInfo extends React.Component { e.preventDefault(); this.props.onChangeVisibility(false); } + handlePressVisible (e) { + if (e.key === ' ' || e.key === 'Enter') { + e.preventDefault(); + this.props.onChangeVisibility(true); + } + } + handlePressNotVisible (e) { + if (e.key === ' ' || e.key === 'Enter') { + e.preventDefault(); + this.props.onChangeVisibility(false); + } + } render () { return ( <SpriteInfoComponent @@ -31,6 +45,8 @@ class SpriteInfo extends React.Component { onChangeRotationStyle={this.handleChangeRotationStyle} onClickNotVisible={this.handleClickNotVisible} onClickVisible={this.handleClickVisible} + onPressNotVisible={this.handlePressNotVisible} + onPressVisible={this.handlePressVisible} /> ); } diff --git a/src/css/units.css b/src/css/units.css index 3de6174a2fe38b65148f7d99a5e459950e37160a..3e7f73f36d327fa55f464c4b49577661f58b4799 100644 --- a/src/css/units.css +++ b/src/css/units.css @@ -9,3 +9,7 @@ $stage-menu-height: 2.75rem; $library-header-height: 4.375rem; $form-radius: calc($space / 2); + +/* layout contants from `layout-constants.js` */ +$full-size: 1095px; +$full-size-paint: 1249px; diff --git a/src/index.jsx b/src/index.jsx index 6d301f5168501f6b20252a6d0e034f509c5eea77..e55fc0ef4d29ef81ff2e8f970b9e932a6412a967 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -7,6 +7,11 @@ import ProjectLoaderHOC from './lib/project-loader-hoc.jsx'; import styles from './index.css'; +if (process.env.NODE_ENV === 'production' && typeof window === 'object') { + // Warn before navigating away + window.onbeforeunload = () => true; +} + const App = AppStateHOC(ProjectLoaderHOC(GUI)); const appTarget = document.createElement('div'); diff --git a/src/lib/layout-constants.js b/src/lib/layout-constants.js index 672c8013f58d477ae767a159a3d2af28784d7139..67c2ae44cc0ecc3c8b979626d4ba51e85a176b63 100644 --- a/src/lib/layout-constants.js +++ b/src/lib/layout-constants.js @@ -3,5 +3,6 @@ export default { fullStageHeight: 360, smallerStageWidth: 480 * 0.85, smallerStageHeight: 360 * 0.85, - fullSizeMinWidth: 1096 + fullSizeMinWidth: 1096, + fullSizePaintMinWidth: 1250 }; diff --git a/src/lib/make-toolbox-xml.js b/src/lib/make-toolbox-xml.js index 315e1388c51656e87be18924c559fb506322dc6c..608cf92b3c307efd54e34ae6aa2d26a19e747f6c 100644 --- a/src/lib/make-toolbox-xml.js +++ b/src/lib/make-toolbox-xml.js @@ -2,7 +2,8 @@ const categorySeparator = '<sep gap="36"/>'; const blockSeparator = '<sep gap="36"/>'; // At default scale, about 28px -const motion = ` +const motion = function (targetId) { + return ` <category name="Motion" colour="#4C97FF" secondaryColour="#3373CC"> <block type="motion_movesteps"> <value name="STEPS"> @@ -120,14 +121,16 @@ const motion = ` ${blockSeparator} <block type="motion_setrotationstyle"/> ${blockSeparator} - <block id="xposition" type="motion_xposition"/> - <block id="yposition" type="motion_yposition"/> - <block id="direction" type="motion_direction"/> + <block id="${targetId}_xposition" type="motion_xposition"/> + <block id="${targetId}_yposition" type="motion_yposition"/> + <block id="${targetId}_direction" type="motion_direction"/> ${categorySeparator} </category> -`; + `; +}; -const looks = ` +const looks = function (targetId) { + return ` <category name="Looks" colour="#9966FF" secondaryColour="#774DCB"> <block type="looks_sayforsecs"> <value name="MESSAGE"> @@ -229,15 +232,17 @@ const looks = ` </value> </block> ${blockSeparator} - <block id="costumeorder" type="looks_costumeorder"/> + <block id="${targetId}_costumeorder" type="looks_costumeorder"/> <block id="backdroporder" type="looks_backdroporder"/> <block id="backdropname" type="looks_backdropname"/> - <block id="size" type="looks_size"/> + <block id="${targetId}_size" type="looks_size"/> ${categorySeparator} </category> -`; + `; +}; -const sound = ` +const sound = function () { + return ` <category name="Sound" colour="#D65CD6" secondaryColour="#BD42BD"> <block type="sound_play"> <value name="SOUND_MENU"> @@ -284,9 +289,11 @@ const sound = ` <block id="volume" type="sound_volume"/> ${categorySeparator} </category> -`; + `; +}; -const events = ` +const events = function () { + return ` <category name="Events" colour="#FFD500" secondaryColour="#CC9900"> <block type="event_whenflagclicked"/> <block type="event_whenkeypressed"> @@ -317,9 +324,11 @@ const events = ` </block> ${categorySeparator} </category> -`; + `; +}; -const control = ` +const control = function () { + return ` <category name="Control" colour="#FFAB19" secondaryColour="#CF8B17"> <block type="control_wait"> <value name="DURATION"> @@ -354,9 +363,11 @@ const control = ` <block type="control_delete_this_clone"/> ${categorySeparator} </category> -`; + `; +}; -const sensing = ` +const sensing = function () { + return ` <category name="Sensing" colour="#4CBFE6" secondaryColour="#2E8EB8"> <block type="sensing_touchingobject"> <value name="TOUCHINGOBJECTMENU"> @@ -422,9 +433,11 @@ const sensing = ` <block type="sensing_dayssince2000"/> ${categorySeparator} </category> -`; + `; +}; -const operators = ` +const operators = function () { + return ` <category name="Operators" colour="#40BF4A" secondaryColour="#389438"> <block type="operator_add"> <value name="NUM1"> @@ -602,33 +615,37 @@ const operators = ` </block> ${categorySeparator} </category> -`; + `; +}; -const data = ` +const data = function () { + return ` <category name="Data" colour="#FF8C1A" secondaryColour="#DB6E00" custom="VARIABLE"> </category> -`; + `; +}; const xmlOpen = '<xml style="display: none">'; const xmlClose = '</xml>'; /** + * @param {!string} targetId - The current editing target * @param {string?} categoriesXML - null for default toolbox, or an XML string with <category> elements. * @returns {string} - a ScratchBlocks-style XML document for the contents of the toolbox. */ -const makeToolboxXML = function (categoriesXML) { +const makeToolboxXML = function (targetId, categoriesXML) { const gap = [categorySeparator]; const everything = [ xmlOpen, - motion, gap, - looks, gap, - sound, gap, - events, gap, - control, gap, - sensing, gap, - operators, gap, - data + motion(targetId), gap, + looks(targetId), gap, + sound(targetId), gap, + events(targetId), gap, + control(targetId), gap, + sensing(targetId), gap, + operators(targetId), gap, + data(targetId) ]; if (categoriesXML) { diff --git a/src/reducers/toolbox.js b/src/reducers/toolbox.js index 4b06a807cac5e10ebb0d643ab06eea39e5b56b77..3f559884f0771545acc67eacf867adcc0274f994 100644 --- a/src/reducers/toolbox.js +++ b/src/reducers/toolbox.js @@ -1,9 +1,9 @@ const UPDATE_TOOLBOX = 'scratch-gui/toolbox/UPDATE_TOOLBOX'; - import makeToolboxXML from '../lib/make-toolbox-xml'; const initialState = { - toolboxXML: makeToolboxXML() + // @todo switch this to make the stage's XML + toolboxXML: makeToolboxXML('') }; const reducer = function (state, action) { diff --git a/test/helpers/selenium-helper.js b/test/helpers/selenium-helper.js index c4f36ca05b712e36c30246c269d47c6c1afa78ce..ce3f04fbf2898469dd3b3774cd83ef2b79603e52 100644 --- a/test/helpers/selenium-helper.js +++ b/test/helpers/selenium-helper.js @@ -4,7 +4,7 @@ import bindAll from 'lodash.bindall'; import 'chromedriver'; // register path import webdriver from 'selenium-webdriver'; -const {By, until} = webdriver; +const {By, until, Button} = webdriver; class SeleniumHelper { constructor () { @@ -15,7 +15,9 @@ class SeleniumHelper { 'findByText', 'findByXpath', 'getDriver', - 'getLogs' + 'getLogs', + 'loadUri', + 'rightClickText' ]); } @@ -34,6 +36,14 @@ class SeleniumHelper { return this.findByXpath(`//body//${scope || '*'}//*[contains(text(), '${text}')]`); } + loadUri (uri) { + return this.driver + .get(`file://${uri}`) + .then(() => ( + this.driver.executeScript('window.onbeforeunload = undefined;') + )); + } + clickXpath (xpath) { return this.findByXpath(xpath).then(el => el.click()); } @@ -42,6 +52,12 @@ class SeleniumHelper { return this.findByText(text, scope).then(el => el.click()); } + rightClickText (text, scope) { + return this.findByText(text, scope).then(el => this.driver.actions() + .click(el, Button.RIGHT) + .perform()); + } + clickButton (text) { return this.clickXpath(`//button[contains(text(), '${text}')]`); } diff --git a/test/integration/examples.test.js b/test/integration/examples.test.js index 7fffa903a4bb6f99f08ab18285869ce7bbd05f17..908dbfd36a3ec145bc79b42eb0ad464190959180 100644 --- a/test/integration/examples.test.js +++ b/test/integration/examples.test.js @@ -9,7 +9,8 @@ const { clickXpath, findByXpath, getDriver, - getLogs + getLogs, + loadUri } = new SeleniumHelper(); const errorWhitelist = [ @@ -31,7 +32,7 @@ describe('player example', () => { test('Load a project by ID', async () => { const projectId = '96708228'; - await driver.get(`file://${uri}#${projectId}`); + await loadUri(`${uri}#${projectId}`); await new Promise(resolve => setTimeout(resolve, 2000)); await clickXpath('//img[@title="Go"]'); await new Promise(resolve => setTimeout(resolve, 2000)); @@ -54,7 +55,7 @@ describe('blocks example', () => { test('Load a project by ID', async () => { const projectId = '96708228'; - await driver.get(`file://${uri}#${projectId}`); + await loadUri(`${uri}#${projectId}`); await new Promise(resolve => setTimeout(resolve, 2000)); await clickXpath('//img[@title="Go"]'); await new Promise(resolve => setTimeout(resolve, 2000)); @@ -64,7 +65,7 @@ describe('blocks example', () => { }); test('Change categories', async () => { - await driver.get(`file://${uri}`); + await loadUri(`${uri}`); await clickText('Looks'); await clickText('Sound'); await clickText('Events'); diff --git a/test/integration/test.js b/test/integration/test.js index 86729cf277017b5cb99f9b858514633583564d47..cfea4e2176921c0d889e619500c50365197bc3eb 100644 --- a/test/integration/test.js +++ b/test/integration/test.js @@ -8,7 +8,9 @@ const { findByText, findByXpath, getDriver, - getLogs + getLogs, + loadUri, + rightClickText } = new SeleniumHelper(); const uri = path.resolve(__dirname, '../../build/index.html'); @@ -34,9 +36,8 @@ describe('costumes, sounds and variables', () => { await driver.quit(); }); - test('Blocks report when clicked in the toolbox', async () => { - await driver.get(`file://${uri}`); + await loadUri(uri); await clickText('Blocks'); await clickText('Operators', blocksTabScope); await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for scroll animation @@ -47,7 +48,7 @@ describe('costumes, sounds and variables', () => { }); test('Switching sprites updates the block menus', async () => { - await driver.get(`file://${uri}`); + await loadUri(uri); await clickText('Sound', blocksTabScope); await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for scroll animation // "meow" sound block should be visible @@ -60,7 +61,7 @@ describe('costumes, sounds and variables', () => { }); test('Adding a costume', async () => { - await driver.get(`file://${uri}`); + await loadUri(uri); await clickText('Costumes'); await clickText('Add Costume'); const el = await findByXpath("//input[@placeholder='what are you looking for?']"); @@ -73,17 +74,22 @@ describe('costumes, sounds and variables', () => { }); test('Adding a sound', async () => { - await driver.get(`file://${uri}`); + await loadUri(uri); await clickText('Sounds'); + + // Delete the sound + await rightClickText('meow', soundsTabScope); + await clickText('delete', soundsTabScope); + await driver.switchTo().alert() + .accept(); + + // Add a sound await clickText('Add Sound'); const el = await findByXpath("//input[@placeholder='what are you looking for?']"); await el.sendKeys('chom'); await clickText('chomp'); // Should close the modal, then click the sounds in the selector - await clickText('meow', soundsTabScope); await clickText('chomp', soundsTabScope); await clickXpath('//button[@title="Play"]'); - await clickText('meow', soundsTabScope); - await clickXpath('//button[@title="Play"]'); await clickText('Louder'); await clickText('Softer'); @@ -99,7 +105,7 @@ describe('costumes, sounds and variables', () => { test('Load a project by ID', async () => { const projectId = '96708228'; - await driver.get(`file://${uri}#${projectId}`); + await loadUri(`${uri}#${projectId}`); await new Promise(resolve => setTimeout(resolve, 2000)); await clickXpath('//img[@title="Go"]'); await new Promise(resolve => setTimeout(resolve, 2000)); @@ -109,7 +115,7 @@ describe('costumes, sounds and variables', () => { }); test('Creating variables', async () => { - await driver.get(`file://${uri}`); + await loadUri(uri); await clickText('Blocks'); await clickText('Data', blocksTabScope); await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for scroll animation @@ -133,7 +139,7 @@ describe('costumes, sounds and variables', () => { }); test('Importing extensions', async () => { - await driver.get(`file://${uri}`); + await loadUri(uri); await clickText('Blocks'); await clickText('Extensions'); await clickText('Pen', modalScope); // Modal closes diff --git a/test/unit/components/__snapshots__/icon-button.test.jsx.snap b/test/unit/components/__snapshots__/icon-button.test.jsx.snap index 3a26f0acf165ca366275b154a43c236ff45ec7d3..b5f3e7a1090bae6573458341790daeeba665566d 100644 --- a/test/unit/components/__snapshots__/icon-button.test.jsx.snap +++ b/test/unit/components/__snapshots__/icon-button.test.jsx.snap @@ -4,6 +4,7 @@ exports[`IconButtonComponent matches snapshot 1`] = ` <div className="custom-class-name" onClick={[Function]} + role="button" > <img className={undefined} diff --git a/test/unit/components/__snapshots__/sound-editor.test.jsx.snap b/test/unit/components/__snapshots__/sound-editor.test.jsx.snap index 6e863223f1d8753ce674382f29db0153edf82b65..693cf59cf31bcc3271be429d4976f391cec1a1f8 100644 --- a/test/unit/components/__snapshots__/sound-editor.test.jsx.snap +++ b/test/unit/components/__snapshots__/sound-editor.test.jsx.snap @@ -341,6 +341,7 @@ exports[`Sound Editor Component matches snapshot 1`] = ` <div className="" onClick={[Function]} + role="button" > <img className={undefined} @@ -357,6 +358,7 @@ exports[`Sound Editor Component matches snapshot 1`] = ` <div className="" onClick={[Function]} + role="button" > <img className={undefined} @@ -373,6 +375,7 @@ exports[`Sound Editor Component matches snapshot 1`] = ` <div className="" onClick={[Function]} + role="button" > <img className={undefined} @@ -389,6 +392,7 @@ exports[`Sound Editor Component matches snapshot 1`] = ` <div className="" onClick={[Function]} + role="button" > <img className={undefined} @@ -405,6 +409,7 @@ exports[`Sound Editor Component matches snapshot 1`] = ` <div className="" onClick={[Function]} + role="button" > <img className={undefined} @@ -421,6 +426,7 @@ exports[`Sound Editor Component matches snapshot 1`] = ` <div className="" onClick={[Function]} + role="button" > <img className={undefined} @@ -437,6 +443,7 @@ exports[`Sound Editor Component matches snapshot 1`] = ` <div className="" onClick={[Function]} + role="button" > <img className={undefined} diff --git a/test/unit/components/__snapshots__/sprite-selector-item.test.jsx.snap b/test/unit/components/__snapshots__/sprite-selector-item.test.jsx.snap index d30617f9aa25d3bcec9972f271d2cf7d76aeaf7d..22b12a467fe94868401db9c26d292214f153f259 100644 --- a/test/unit/components/__snapshots__/sprite-selector-item.test.jsx.snap +++ b/test/unit/components/__snapshots__/sprite-selector-item.test.jsx.snap @@ -12,8 +12,10 @@ exports[`SpriteSelectorItemComponent matches snapshot when selected 1`] = ` onTouchStart={[Function]} > <div + aria-label="Close" className="" onClick={[Function]} + role="button" > <img className={undefined}