diff --git a/src/components/import-error/import-error.jsx b/src/components/import-error/import-error.jsx index bc8f287ea1f3df616dc264f9f9ef2260e4639253..af677e4a26a3b59c4fc6f1179ee379ac2c3850df 100644 --- a/src/components/import-error/import-error.jsx +++ b/src/components/import-error/import-error.jsx @@ -1,6 +1,6 @@ import bindAll from 'lodash.bindall'; import classNames from 'classnames'; -import {/* defineMessages, */injectIntl/* , intlShape, FormattedMessage*/} from 'react-intl'; +import {defineMessages, injectIntl, FormattedMessage} from 'react-intl'; import PropTypes from 'prop-types'; import React from 'react'; import ReactTooltip from 'react-tooltip'; @@ -10,6 +10,17 @@ import styles from './import-error.css'; // TODO store different error messages depending on the situation (?) and // needs to use intl lib for localization support +// TODO error tooltip should be always visible in the error state, +// instead of popping up hen hovering over the input + +const messages = defineMessages({ + invalidLink: { + id: 'gui.importError.invalidLink', + defaultMessage: 'Uh oh, that link doesn\'t look quite right.', + description: 'Invalid link error message' + } +}); + class ImportErrorContent extends React.Component { constructor (props) { super(props); @@ -30,8 +41,13 @@ class ImportErrorContent extends React.Component { this.setState({isShowing: false}); } getContent () { + const messageId = this.props.errorMessage; return ( - <p>{this.props.errorMessage}</p> + <p> + <FormattedMessage + {...messages[`${messageId}`]} + /> + </p> ); } render () { @@ -60,7 +76,6 @@ class ImportErrorContent extends React.Component { ImportErrorContent.propTypes = { className: PropTypes.string, errorMessage: PropTypes.string.isRequired, - // intl: intlShape, place: PropTypes.oneOf(['top', 'right', 'bottom', 'left']), tooltipId: PropTypes.string.isRequired diff --git a/src/components/import-input/import-input.jsx b/src/components/import-input/import-input.jsx index bc4d7c0c2b220622c5fe2414a987002cb5c427c2..21ea902a0e38c5582026ff637f2d0698b2770e9c 100644 --- a/src/components/import-input/import-input.jsx +++ b/src/components/import-input/import-input.jsx @@ -1,10 +1,11 @@ import PropTypes from 'prop-types'; import React from 'react'; -// import classNames from 'classnames'; import {ImportErrorTooltip} from '../import-error/import-error.jsx'; +// TODO error tooltip needs to go around both the input and the button +// error tooltip should also be always visible class ImportInput extends React.Component { render () { let input = null; diff --git a/src/containers/import-modal.jsx b/src/containers/import-modal.jsx index e48df8e5a7004a63ffdd52b78948cc5006d6d825..169378e1a24a0e84c4de306945f112a5461ac4cd 100644 --- a/src/containers/import-modal.jsx +++ b/src/containers/import-modal.jsx @@ -2,12 +2,8 @@ import bindAll from 'lodash.bindall'; import PropTypes from 'prop-types'; import React from 'react'; import {connect} from 'react-redux'; -import platform from 'platform'; import ImportModalComponent from '../components/import-modal/import-modal.jsx'; -import BrowserModalComponent from '../components/browser-modal/browser-modal.jsx'; - -import log from '../lib/log'; import { closeImportInfo @@ -44,10 +40,10 @@ class ImportModal extends React.Component { document.body.removeChild(projectLink); this.props.onViewProject(); } else { + // TODO handle error messages and error states this.setState({ hasValidationError: true, - errorMessage: `Uh oh, that link doesn't look quite right.`}); - log.info('Invalid link error'); + errorMessage: `invalidLink`}); } } handleChange (e) { @@ -56,7 +52,6 @@ class ImportModal extends React.Component { validate (input) { const matches = input.match(/^(https:\/\/)?scratch\.mit\.edu\/projects\/(\d+)(\/?)$/); if (matches && matches.length > 0) { - log.info(`Project ID: ${matches[2]}`); return matches[2]; } return null; @@ -65,18 +60,14 @@ class ImportModal extends React.Component { this.props.onCancel(); } handleGoBack () { + // TODO what should the go back button actually do? Should it bring the preview modal + // back up or just close this modal, or go back to scratch? window.location.replace('https://scratch.mit.edu'); } // TODO not sure if we need this, since it shouldn't be possible to bring up this // modal without first going through the preview modal - supportedBrowser () { - if (platform.name === 'IE') { - return false; - } - return true; - } render () { - return (this.supportedBrowser() ? + return ( <ImportModalComponent errorMessage={this.state.errorMessage} hasValidationError={this.state.hasValidationError} @@ -86,9 +77,6 @@ class ImportModal extends React.Component { onChange={this.handleChange} onKeyPress={this.handleKeyPress} onViewProject={this.handleViewProject} - /> : - <BrowserModalComponent - onBack={this.handleGoBack} /> ); } diff --git a/test/integration/project-loading.test.js b/test/integration/project-loading.test.js index 9d5cb0384252aa9aacb96bd3a780173050dda366..6add67c42b178055b653bf94cf9a29c41a5db9cc 100644 --- a/test/integration/project-loading.test.js +++ b/test/integration/project-loading.test.js @@ -4,6 +4,7 @@ import SeleniumHelper from '../helpers/selenium-helper'; const { clickText, clickXpath, + findByXpath, getDriver, getLogs, loadUri @@ -31,7 +32,40 @@ describe('Loading scratch gui', () => { describe('Loading projects by ID', () => { - test('Load a project by ID', async () => { + test('Load 2.0 project using import modal', async () => { + await loadUri(uri); + await clickText('View 2.0 Project'); + const el = await findByXpath("//input[@placeholder='scratch.mit.edu/projects/123456789']"); + const projectId = '96708228'; + await el.sendKeys(`scratch.mit.edu/projects/${projectId}`); + await clickXpath('//button[@title="viewproject"]'); + await new Promise(resolve => setTimeout(resolve, 2000)); + await clickXpath('//img[@title="Go"]'); + await new Promise(resolve => setTimeout(resolve, 2000)); + await clickXpath('//img[@title="Stop"]'); + const logs = await getLogs(); + await expect(logs).toEqual([]); + }); + + test('Invalid url when loading project through modal lets you try again', async () => { + await loadUri(uri); + await clickText('View 2.0 Project'); + let el = await findByXpath("//input[@placeholder='scratch.mit.edu/projects/123456789']"); + await el.sendKeys('thisisnotaurl'); + await clickXpath('//button[@title="viewproject"]'); + el = await findByXpath("//input[@placeholder='scratch.mit.edu/projects/123456789']"); + await el.clear(); + await el.sendKeys('scratch.mit.edu/projects/96708228'); + await clickXpath('//button[@title="viewproject"]'); + await new Promise(resolve => setTimeout(resolve, 2000)); + await clickXpath('//img[@title="Go"]'); + await new Promise(resolve => setTimeout(resolve, 2000)); + await clickXpath('//img[@title="Stop"]'); + const logs = await getLogs(); + await expect(logs).toEqual([]); + }); + + test('Load a project by ID directly through url', async () => { const projectId = '96708228'; await loadUri(`${uri}#${projectId}`); await clickXpath('//button[@title="tryit"]');