Skip to content

Commit

Permalink
Merge pull request #13951 from code-dot-org/maker-testable-setup-comp…
Browse files Browse the repository at this point in the history
…onent

Maker: Test setup checklist component
  • Loading branch information
islemaster committed Mar 23, 2017
2 parents 4348823 + 9c340a1 commit acb765f
Show file tree
Hide file tree
Showing 5 changed files with 286 additions and 122 deletions.
@@ -1,7 +1,7 @@
/** @file Maker Board setup checker */
import React, {Component} from 'react';
import CircuitPlaygroundBoard from '../CircuitPlaygroundBoard';
import {ensureAppInstalled, findPortWithViableDevice} from '../portScanning';
import React, {Component, PropTypes} from 'react';
import SetupChecker from '../util/SetupChecker';
import {isWindows, isChrome, getChromeVersion} from '../util/browserChecks';
import SetupStep, {
HIDDEN,
WAITING,
Expand Down Expand Up @@ -29,9 +29,14 @@ const initialState = {
[STATUS_BOARD_COMPONENTS]: WAITING,
};

export default class BoardSetupCheck extends Component {
export default class SetupChecklist extends Component {
state = {...initialState};

static propTypes = {
setupChecker: PropTypes.instanceOf(SetupChecker).isRequired,
stepDelay: PropTypes.number,
};

hide(selector) {
this.setState({[selector]: HIDDEN});
}
Expand Down Expand Up @@ -61,36 +66,39 @@ export default class BoardSetupCheck extends Component {
}

detect() {
const {setupChecker} = this.props;
this.setState({...initialState, isDetecting: true});

if (!isWindows()) {
this.hide(STATUS_WINDOWS_DRIVERS);
}

let portName = null;
let boardController = null;

Promise.resolve()

// Are we using a compatible browser?
.then(() => this.detectChromeVersion())
.then(() => this.detectStep(STATUS_IS_CHROME,
() => setupChecker.detectChromeVersion()))

// Is Chrome App Installed?
.then(() => this.detectChromeAppInstalled())
.then(() => this.detectStep(STATUS_APP_INSTALLED,
() => setupChecker.detectChromeAppInstalled()))

// Is board plugged in?
.then(() => this.detectBoardPluggedIn())
.then(usablePort => portName = usablePort)
.then(() => this.detectStep(STATUS_BOARD_PLUG,
() => setupChecker.detectBoardPluggedIn()))

// Can we talk to the firmware?
.then(() => this.detectCorrectFirmware(portName))
.then(board => boardController = board)
.then(() => this.detectStep(STATUS_BOARD_CONNECT,
() => setupChecker.detectCorrectFirmware()))

// Can we initialize components successfully?
.then(() => this.detectComponentsInitialize(boardController))
.then(() => this.detectStep(STATUS_BOARD_COMPONENTS,
() => setupChecker.detectComponentsInitialize()))

// Everything looks good, let's par-tay!
.then(() => this.celebrate(boardController))
.then(() => this.thumb(STATUS_BOARD_COMPONENTS))
.then(() => setupChecker.celebrate())
.then(() => this.succeed(STATUS_BOARD_COMPONENTS))

// If anything goes wrong along the way, we'll end up in this
// catch clause - make sure to report the error out.
Expand All @@ -103,106 +111,28 @@ export default class BoardSetupCheck extends Component {

// Finally...
.then(() => {
if (boardController) {
boardController.destroy();
boardController = null;
}
portName = null;
setupChecker.teardown();
this.setState({isDetecting: false});
});
}

detectChromeVersion() {
this.spin(STATUS_IS_CHROME);
return promiseWaitFor(200)
.then(() => {
if (!isChrome()) {
return Promise.reject(new Error('Not using Chrome'));
}

if (!gtChrome33()) {
return Promise.reject(new Error('Not using Chrome > v33'));
}

this.succeed(STATUS_IS_CHROME);
})
.catch(error => {
this.fail(STATUS_IS_CHROME);
return Promise.reject(error);
});
}

/**
* @return {Promise}
*/
detectChromeAppInstalled() {
this.spin(STATUS_APP_INSTALLED);
return promiseWaitFor(200)
.then(ensureAppInstalled)
.then(() => this.succeed(STATUS_APP_INSTALLED))
.catch(error => {
this.fail(STATUS_APP_INSTALLED);
return Promise.reject(error);
});
}

/**
* @return {Promise.<string>} Resolves to usable port name
*/
detectBoardPluggedIn() {
this.spin(STATUS_BOARD_PLUG);
return promiseWaitFor(200)
.then(findPortWithViableDevice)
.then(portName => {
this.succeed(STATUS_BOARD_PLUG);
return portName;
})
.catch(error => {
this.fail(STATUS_BOARD_PLUG);
return Promise.reject(error);
});
}

/**
* @return {Promise.<CircuitPlaygroundBoard>}
*/
detectCorrectFirmware(portName) {
this.spin(STATUS_BOARD_CONNECT);
const boardController = new CircuitPlaygroundBoard(portName);
return boardController.connectToFirmware()
.then(() => {
this.succeed(STATUS_BOARD_CONNECT);
return boardController;
})
.catch(error => {
this.fail(STATUS_BOARD_CONNECT);
return Promise.reject(error);
});
}

/**
* Perform the work to check a step, wrapped in appropriate status changes.
* @param {string} stepKey
* @param {function:Promise} stepWork
* @return {Promise}
*/
detectComponentsInitialize(boardController) {
this.spin(STATUS_BOARD_COMPONENTS);
return promiseWaitFor(200)
.then(() => boardController.initializeComponents())
.then(() => this.succeed(STATUS_BOARD_COMPONENTS))
detectStep(stepKey, stepWork) {
this.spin(stepKey);
return promiseWaitFor(this.props.stepDelay || 200)
.then(stepWork)
.then(() => this.succeed(stepKey))
.catch(error => {
this.fail(STATUS_BOARD_COMPONENTS);
this.fail(stepKey);
return Promise.reject(error);
});
}

/**
* @return {Promise}
*/
celebrate(boardController) {
this.thumb(STATUS_BOARD_COMPONENTS);
return boardController.celebrateSuccessfulConnection()
.then(() => this.succeed(STATUS_BOARD_COMPONENTS));
}

/**
* Helper to be used on second/subsequent attempts at detecing board usability.
*/
Expand Down Expand Up @@ -313,20 +243,3 @@ function promiseWaitFor(ms) {
setTimeout(resolve, ms);
});
}

function isChrome() {
return !!window.chrome;
}

function getChromeVersion() {
const raw = navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./);
return raw ? parseInt(raw[2], 10) : false;
}

function gtChrome33() {
return getChromeVersion() >= 33;
}

function isWindows() {
return navigator.platform.indexOf('Win') > -1;
}
74 changes: 74 additions & 0 deletions apps/src/lib/kits/maker/util/SetupChecker.js
@@ -0,0 +1,74 @@
/** @file Stubbable core setup check behavior for the setup page. */
import CircuitPlaygroundBoard from '../CircuitPlaygroundBoard';
import {ensureAppInstalled, findPortWithViableDevice} from '../portScanning';
import {isChrome, gtChrome33} from './browserChecks';

export default class SetupChecker {
port = null;
boardController = null;

/**
* Resolve if using Chrome > 33
* @return {Promise}
*/
detectChromeVersion() {
return new Promise((resolve, reject) => {
if (!isChrome()) {
reject(new Error('Not using Chrome'));
} if (!gtChrome33()) {
reject(new Error('Not using Chrome > v33'));
} else {
resolve();
}
});
}

/**
* Resolve if the Chrome Connector App is installed.
* @return {Promise}
*/
detectChromeAppInstalled() {
return ensureAppInstalled();
}

/**
* @return {Promise}
*/
detectBoardPluggedIn() {
return findPortWithViableDevice()
.then(port => this.port = port);
}

/**
* @return {Promise}
*/
detectCorrectFirmware() {
this.boardController = new CircuitPlaygroundBoard(this.port);
return this.boardController.connectToFirmware();
}

/**
* @return {Promise}
*/
detectComponentsInitialize() {
return new Promise(resolve => {
this.boardController.initializeComponents();
resolve();
});
}

/**
* @return {Promise}
*/
celebrate() {
return this.boardController.celebrateSuccessfulConnection();
}

teardown() {
if (this.boardController) {
this.boardController.destroy();
}
this.boardController = null;
this.port = null;
}
}
18 changes: 18 additions & 0 deletions apps/src/lib/kits/maker/util/browserChecks.js
@@ -0,0 +1,18 @@
/** @file Some misc. browser check methods for maker */

export function gtChrome33() {
return getChromeVersion() >= 33;
}

export function isChrome() {
return !!window.chrome;
}

export function getChromeVersion() {
const raw = navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./);
return raw ? parseInt(raw[2], 10) : false;
}

export function isWindows() {
return navigator.platform.indexOf('Win') > -1;
}
9 changes: 7 additions & 2 deletions apps/src/sites/studio/pages/maker/setup.js
@@ -1,8 +1,13 @@
import React from 'react';
import ReactDOM from 'react-dom';
import BoardSetupCheck from '@cdo/apps/lib/kits/maker/ui/BoardSetupCheck';
import SetupChecklist from '@cdo/apps/lib/kits/maker/ui/SetupChecklist';
import SetupChecker from '@cdo/apps/lib/kits/maker/util/SetupChecker';

$(function () {
ReactDOM.render(<BoardSetupCheck/>, document.getElementById('setup-status-mount'));
const setupChecker = new SetupChecker();
ReactDOM.render(
<SetupChecklist setupChecker={setupChecker}/>,
document.getElementById('setup-status-mount')
);
$('.maker-setup a').attr('target', '_blank');
});

0 comments on commit acb765f

Please sign in to comment.