Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,5 @@ npm-*

# Downloaded during "npm install"
/static/microbit

/tmp
36 changes: 36 additions & 0 deletions src/lib/project-fetcher-hoc.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import {intlShape, injectIntl} from 'react-intl';
import bindAll from 'lodash.bindall';
import {connect} from 'react-redux';
import xhr from 'xhr';

import {setProjectUnchanged} from '../reducers/project-changed';
import {
Expand Down Expand Up @@ -72,6 +73,41 @@ const ProjectFetcherHOC = function (WrappedComponent) {
}
}
fetchProject (projectId, loadingState) {
if (!this.props.projectToken) {
const errorHandler = err => {
this.props.onError(err);
log.error(err);
};
return new Promise((resolve, reject) => {
const options = {
method: 'GET',
uri: `https://api.smalruby.app/scratch-api-proxy/projects/${projectId}`,
json: true
};
xhr(options, (error, response) => {
if (error || response.statusCode !== 200) {
return reject(new Error(response.status));
}
resolve(response.body.project_token);
});
})
.then(projectToken => {
storage.setProjectToken(projectToken);
storage
.load(storage.AssetType.Project, projectId, storage.DataFormat.JSON)
.then(projectAsset => {
if (projectAsset) {
this.props.onFetchedProjectData(projectAsset.data, loadingState);
} else {
// Treat failure to load as an error
// Throw to be caught by catch later on
throw new Error('Could not find project');
}
})
.catch(errorHandler);
}, errorHandler)
.catch(errorHandler);
}
return storage
.load(storage.AssetType.Project, projectId, storage.DataFormat.JSON)
.then(projectAsset => {
Expand Down
17 changes: 16 additions & 1 deletion test/integration/blocks.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const {
getLogs,
Key,
loadUri,
notExistsByXpath,
rightClickText,
scope
} = new SeleniumHelper();
Expand All @@ -35,6 +36,7 @@ describe('Working with the blocks', () => {

test('Blocks report when clicked in the toolbox', async () => {
await loadUri(uri);
await notExistsByXpath('//*[div[contains(@class, "loader_background")]]');
await clickText('Code');
await clickBlocksCategory('Operators');
await clickText('join', scope.blocksTab); // Click "join <hello> <world>" block
Expand All @@ -45,6 +47,7 @@ describe('Working with the blocks', () => {

test('Switching sprites updates the block menus', async () => {
await loadUri(uri);
await notExistsByXpath('//*[div[contains(@class, "loader_background")]]');
await clickBlocksCategory('Sound');
// "Meow" sound block should be visible
await findByText('Meow', scope.blocksTab);
Expand All @@ -60,6 +63,7 @@ describe('Working with the blocks', () => {

test('Creating variables', async () => {
await loadUri(uri);
await notExistsByXpath('//*[div[contains(@class, "loader_background")]]');
await clickText('Code');
await clickBlocksCategory('Variables');

Expand Down Expand Up @@ -107,6 +111,7 @@ describe('Working with the blocks', () => {

test('Creating a list', async () => {
await loadUri(uri);
await notExistsByXpath('//*[div[contains(@class, "loader_background")]]');
await clickText('Code');
await clickBlocksCategory('Variables');

Expand Down Expand Up @@ -147,6 +152,7 @@ describe('Working with the blocks', () => {

test('Custom procedures', async () => {
await loadUri(uri);
await notExistsByXpath('//*[div[contains(@class, "loader_background")]]');
await clickBlocksCategory('My Blocks');
await clickText('Make a Block');
// Click on the "add an input" buttons
Expand All @@ -164,6 +170,7 @@ describe('Working with the blocks', () => {

test('Adding an extension', async () => {
await loadUri(uri);
await notExistsByXpath('//*[div[contains(@class, "loader_background")]]');
await clickXpath('//button[@title="Add Extension"]');

await clickText('Pen');
Expand All @@ -177,6 +184,7 @@ describe('Working with the blocks', () => {

test('Record option from sound block menu opens sound recorder', async () => {
await loadUri(uri);
await notExistsByXpath('//*[div[contains(@class, "loader_background")]]');
await clickText('Code');
await clickBlocksCategory('Sound');
await clickText('Meow', scope.blocksTab); // Click "play sound <Meow> until done" block
Expand All @@ -192,6 +200,7 @@ describe('Working with the blocks', () => {

test('Renaming costume changes the default costume name in the toolbox', async () => {
await loadUri(uri);
await notExistsByXpath('//*[div[contains(@class, "loader_background")]]');

// Rename the costume
await clickText('Costumes');
Expand All @@ -209,8 +218,9 @@ describe('Working with the blocks', () => {
await clickText('newname', scope.blocksTab);
});

test.skip('Renaming costume with a special character should not break toolbox', async () => {
test('Renaming costume with a special character should not break toolbox', async () => {
await loadUri(uri);
await notExistsByXpath('//*[div[contains(@class, "loader_background")]]');

// Rename the costume
await clickText('Costumes');
Expand All @@ -232,6 +242,7 @@ describe('Working with the blocks', () => {

test('Adding costumes DOES update the default costume name in the toolbox', async () => {
await loadUri(uri);
await notExistsByXpath('//*[div[contains(@class, "loader_background")]]');

// By default, costume2 is in the costume tab
await clickBlocksCategory('Looks');
Expand All @@ -256,6 +267,7 @@ describe('Working with the blocks', () => {
// Skipped because it was flakey on travis, but seems to run locally ok
test('Adding a sound DOES update the default sound name in the toolbox', async () => {
await loadUri(uri);
await notExistsByXpath('//*[div[contains(@class, "loader_background")]]');
await clickText('Sounds');
await clickXpath('//button[@aria-label="Choose a Sound"]');
await clickText('A Bass', scope.modal); // Should close the modal
Expand All @@ -271,6 +283,7 @@ describe('Working with the blocks', () => {
test('"See inside" after being on project page re-initializing variables', async () => {
const playerUri = path.resolve(__dirname, '../../build/player.html');
await loadUri(playerUri);
await notExistsByXpath('//*[div[contains(@class, "loader_background")]]');
await clickText('See inside');
await clickBlocksCategory('Variables');
await clickText('my\u00A0variable');
Expand All @@ -285,6 +298,7 @@ describe('Working with the blocks', () => {
// Regression test for switching editor tabs causing toolbox to stop updating
test('Creating variables after adding extensions updates the toolbox', async () => {
await loadUri(uri);
await notExistsByXpath('//*[div[contains(@class, "loader_background")]]');
await clickText('Costumes');
await clickText('Code');
await clickBlocksCategory('Variables');
Expand All @@ -300,6 +314,7 @@ describe('Working with the blocks', () => {
const changeVariableByScope = "*[@data-id='data_changevariableby']";

await loadUri(uri);
await notExistsByXpath('//*[div[contains(@class, "loader_background")]]');

await clickText('Code');
await clickBlocksCategory('Variables');
Expand Down
13 changes: 9 additions & 4 deletions test/integration/project-loading.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const {
getDriver,
getLogs,
loadUri,
notExistsByXpath,
scope
} = new SeleniumHelper();

Expand Down Expand Up @@ -79,28 +80,31 @@ describe('Loading scratch gui', () => {
await expect(logs).toEqual([]);
});

// skipping because this test fails frequently on CI; might need "wait(until.elementLocated" or similar
// error message is "stale element reference: element is not attached to the page document"
test.skip('Creating new project resets active tab to Code tab', async () => {
test('Creating new project resets active tab to Code tab', async () => {
await loadUri(uri);
await notExistsByXpath('//*[div[contains(@class, "loader_background")]]');
await findByXpath('//*[span[text()="Costumes"]]');
await clickText('Costumes');
await clickXpath(FILE_MENU_XPATH);
await clickXpath('//li[span[text()="New"]]');
await notExistsByXpath('//*[div[contains(@class, "loader_background")]]');
await findByXpath('//div[@class="scratchCategoryMenu"]');
await clickText('Operators', scope.blocksTab);
});

test('Not logged in->made no changes to project->create new project should not show alert', async () => {
await loadUri(uri);
await notExistsByXpath('//*[div[contains(@class, "loader_background")]]');
await clickXpath(FILE_MENU_XPATH);
await clickXpath('//li[span[text()="New"]]');
await notExistsByXpath('//*[div[contains(@class, "loader_background")]]');
await findByXpath('//*[div[@class="scratchCategoryMenu"]]');
await clickText('Operators', scope.blocksTab);
});

test.skip('Not logged in->made a change to project->create new project should show alert', async () => {
test('Not logged in->made a change to project->create new project should show alert', async () => {
await loadUri(uri);
await notExistsByXpath('//*[div[contains(@class, "loader_background")]]');
await clickText('Sounds');
await clickXpath('//button[@aria-label="Choose a Sound"]');
await clickText('A Bass', scope.modal); // Should close the modal
Expand All @@ -110,6 +114,7 @@ describe('Loading scratch gui', () => {
driver.switchTo()
.alert()
.accept();
await notExistsByXpath('//*[div[contains(@class, "loader_background")]]');
await findByXpath('//*[div[@class="scratchCategoryMenu"]]');
await clickText('Operators', scope.blocksTab);
});
Expand Down
2 changes: 1 addition & 1 deletion test/integration/removed-trademarks.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,5 @@ describe('Removed trademarks (ex: Scratch Cat)', () => {
expect(await notExistsByXpath(`//*[span[contains(text(), "${costumePrefix}")]]`)).toBeTruthy();
searchElement.clear();
}
}, 60 * 1000);
}, 120 * 1000);
});
2 changes: 2 additions & 0 deletions test/integration/ruby-tab/control.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const {
scope,
getDriver,
loadUri,
notExistsByXpath,
urlFor
} = seleniumHelper;

Expand All @@ -34,6 +35,7 @@ describe('Ruby Tab: Control category blocks', () => {

test('Ruby -> Code -> Ruby', async () => {
await loadUri(urlFor('/'));
await notExistsByXpath('//*[div[contains(@class, "loader_background")]]');
await clickXpath('//button[@aria-label="Choose a Sprite"]');
await clickText('Abby', scope.modal);
await findByXpath(
Expand Down
2 changes: 2 additions & 0 deletions test/integration/ruby-tab/motion.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const {
scope,
getDriver,
loadUri,
notExistsByXpath,
urlFor
} = seleniumHelper;

Expand All @@ -30,6 +31,7 @@ describe('Ruby Tab: Motion category blocks', () => {

test('Ruby -> Code -> Ruby', async () => {
await loadUri(urlFor('/'));
await notExistsByXpath('//*[div[contains(@class, "loader_background")]]');
await clickXpath('//button[@aria-label="Choose a Sprite"]');
await clickText('Abby', scope.modal);
await clickText('Sprite1', scope.spriteItems);
Expand Down
2 changes: 2 additions & 0 deletions test/integration/ruby-tab/sensing.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const {
scope,
getDriver,
loadUri,
notExistsByXpath,
urlFor
} = seleniumHelper;

Expand All @@ -30,6 +31,7 @@ describe('Ruby Tab: Control category blocks', () => {

test('Ruby -> Code -> Ruby', async () => {
await loadUri(urlFor('/'));
await notExistsByXpath('//*[div[contains(@class, "loader_background")]]');
await clickXpath('//button[@aria-label="Choose a Sprite"]');
await clickText('Abby', scope.modal);
await clickText('Sprite1', scope.spriteItems);
Expand Down
1 change: 1 addition & 0 deletions test/unit/util/project-fetcher-hoc.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ describe('ProjectFetcherHOC', () => {
);
mounted.setProps({
reduxProjectId: '100',
projectToken: '12345',
isFetchingWithId: true,
loadingState: LoadingState.FETCHING_WITH_ID
});
Expand Down