Merge pull request #3679 from LLK/circleci-semantic-release
Migrate to CircleCI & use semantic release
This commit is contained in:
81 files changed
+40304
-3588
No files matched your search
@@ -0,0 +1,69 @@
|
||||
version: 2.1
|
||||
orbs:
|
||||
node: circleci/node@5
|
||||
aliases:
|
||||
- &release-branches
|
||||
- develop
|
||||
- /^hotfix\//
|
||||
commands:
|
||||
deploy_gh_pages:
|
||||
steps:
|
||||
- run:
|
||||
name: deploy to GH Pages
|
||||
command: |
|
||||
if [ -n "$GH_PAGES_REPO" ]; then
|
||||
export GIT_AUTHOR_EMAIL="$(git log --pretty=format:"%ae" -n1)"
|
||||
export GIT_AUTHOR_NAME="$(git log --pretty=format:"%an" -n1)"
|
||||
export GIT_COMMITTER_EMAIL="${GIT_AUTHOR_EMAIL}"
|
||||
export GIT_COMMITTER_NAME="${GIT_AUTHOR_NAME}"
|
||||
npm run deploy -x -r $GH_PAGES_REPO
|
||||
fi
|
||||
jobs:
|
||||
build_no_release:
|
||||
executor: node/default # defaults to LTS
|
||||
steps:
|
||||
- checkout
|
||||
- node/install-packages
|
||||
- run: npm test
|
||||
- run: npx commitlint --from=`git merge-base HEAD develop`
|
||||
- run: npm run build
|
||||
- deploy_gh_pages
|
||||
build_and_release:
|
||||
executor: node/default # defaults to LTS
|
||||
steps:
|
||||
- checkout
|
||||
- node/install-packages
|
||||
- run: npm test
|
||||
- run: npx commitlint --from='HEAD~1'
|
||||
- run: npm run build
|
||||
- deploy_gh_pages
|
||||
- run: npx semantic-release
|
||||
update_i18n:
|
||||
executor: node/default # defaults to LTS
|
||||
steps:
|
||||
- checkout
|
||||
- node/install-packages
|
||||
- run: npm run i18n:src && npm run i18n:push
|
||||
workflows:
|
||||
version: 2
|
||||
build_no_release:
|
||||
jobs:
|
||||
- build_no_release:
|
||||
filters:
|
||||
branches:
|
||||
ignore: *release-branches
|
||||
build_and_release:
|
||||
jobs:
|
||||
- build_and_release:
|
||||
filters:
|
||||
branches:
|
||||
only: *release-branches
|
||||
update_i18n:
|
||||
jobs:
|
||||
- update_i18n
|
||||
triggers:
|
||||
- schedule:
|
||||
cron: 0 0 * * * # daily at midnight UTC = 7-8pm US Eastern
|
||||
filters:
|
||||
branches:
|
||||
only: develop
|
||||
@@ -9,3 +9,7 @@ trim_trailing_whitespace = true
|
||||
|
||||
[*.{js,html}]
|
||||
indent_style = space
|
||||
|
||||
[.circleci/config.yml]
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
@@ -0,0 +1 @@
|
||||
* text eol=lf
|
||||
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
npx --no-install commitlint --edit $1
|
||||
-74
@@ -1,74 +0,0 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- lts/*
|
||||
env:
|
||||
global:
|
||||
- NODE_ENV=production
|
||||
- NPM_TAG=latest
|
||||
- RELEASE_TIMESTAMP="$(date +'%Y%m%d%H%M%S')"
|
||||
matrix:
|
||||
- NPM_SCRIPT="tap:unit -- --jobs=4"
|
||||
- NPM_SCRIPT="tap:integration -- --jobs=4"
|
||||
cache:
|
||||
directories:
|
||||
- "$HOME/.npm"
|
||||
install:
|
||||
- npm ci --production=false
|
||||
script: npm run $NPM_SCRIPT
|
||||
jobs:
|
||||
include:
|
||||
- env: NPM_SCRIPT=lint
|
||||
- env: NPM_SCRIPT=build
|
||||
if: not (type != pull_request AND (branch =~ /^(develop|master|hotfix\/)/))
|
||||
- stage: release
|
||||
env: NPM_SCRIPT=build
|
||||
before_deploy:
|
||||
- >
|
||||
if [ -z "$BEFORE_DEPLOY_RAN" ]; then
|
||||
VPKG=$($(npm bin)/json -f package.json version)
|
||||
export RELEASE_VERSION=${VPKG}-prerelease.${RELEASE_TIMESTAMP}
|
||||
npm --no-git-tag-version version $RELEASE_VERSION
|
||||
if [[ "$TRAVIS_BRANCH" == hotfix/* ]]; then # double brackets are important for matching the wildcard
|
||||
export NPM_TAG=hotfix
|
||||
fi
|
||||
git config --global user.email "$(git log --pretty=format:"%ae" -n1)"
|
||||
git config --global user.name "$(git log --pretty=format:"%an" -n1)"
|
||||
export BEFORE_DEPLOY_RAN=true
|
||||
fi
|
||||
deploy:
|
||||
- provider: npm
|
||||
on:
|
||||
branch:
|
||||
- master
|
||||
- develop
|
||||
- hotfix/*
|
||||
condition: $TRAVIS_EVENT_TYPE != cron
|
||||
skip_cleanup: true
|
||||
email: $NPM_EMAIL
|
||||
api_key: $NPM_TOKEN
|
||||
tag: $NPM_TAG
|
||||
- provider: script
|
||||
on:
|
||||
branch:
|
||||
- master
|
||||
- develop
|
||||
- hotfix/*
|
||||
condition: $TRAVIS_EVENT_TYPE != cron
|
||||
skip_cleanup: true
|
||||
script: if npm info | grep -q $RELEASE_VERSION; then git tag $RELEASE_VERSION && git push https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git $RELEASE_VERSION; fi
|
||||
- provider: script
|
||||
on:
|
||||
all_branches: true
|
||||
condition: $TRAVIS_EVENT_TYPE != cron
|
||||
skip_cleanup: true
|
||||
script: npm run --silent deploy -- -x -r $GH_PAGES_REPO
|
||||
- provider: script
|
||||
on:
|
||||
branch: develop
|
||||
condition: $TRAVIS_EVENT_TYPE == cron
|
||||
skip_cleanup: true
|
||||
script: npm run i18n:src && npm run i18n:push
|
||||
stages:
|
||||
- test
|
||||
- name: release
|
||||
if: type != pull_request AND (branch =~ /^(develop|master|hotfix\/)/)
|
||||
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
extends: ['@commitlint/config-conventional']
|
||||
};
|
||||
Generated
+39988
-3332
File diff suppressed because it is too large.
Load diff
+21
-5
@@ -7,27 +7,39 @@
|
||||
"homepage": "https://github.com/LLK/scratch-vm#readme",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+ssh://git@github.com/LLK/scratch-vm.git"
|
||||
"url": "https://github.com/LLK/scratch-vm.git"
|
||||
},
|
||||
"main": "./dist/node/scratch-vm.js",
|
||||
"browser": "./src/index.js",
|
||||
"scripts": {
|
||||
"build": "npm run docs && webpack --progress --colors --bail",
|
||||
"coverage": "tap ./test/{unit,integration}/*.js --coverage --coverage-report=lcov",
|
||||
"deploy": "touch playground/.nojekyll && gh-pages -t -d playground -m \"Build for $(git log --pretty=format:%H -n1)\"",
|
||||
"deploy": "touch playground/.nojekyll && gh-pages -t -d playground -m \"Build for $(git log -n1 --pretty=format:\"%h %s\") [skip ci]\"",
|
||||
"docs": "jsdoc -c .jsdoc.json",
|
||||
"i18n:src": "mkdirp translations/core && format-message extract --out-file translations/core/en.json src/extensions/**/index.js",
|
||||
"i18n:push": "tx-push-src scratch-editor extensions translations/core/en.json",
|
||||
"lint": "eslint . && format-message lint src/**/*.js",
|
||||
"prepare": "husky install",
|
||||
"prepublish": "in-publish && npm run build || not-in-publish",
|
||||
"start": "webpack-dev-server",
|
||||
"tap": "tap ./test/{unit,integration}/*.js",
|
||||
"tap:unit": "tap ./test/unit/*.js",
|
||||
"tap:integration": "tap ./test/integration/*.js",
|
||||
"test": "npm run lint && npm run docs && npm run tap",
|
||||
"test": "npm run lint && npm run tap",
|
||||
"watch": "webpack --progress --colors --watch",
|
||||
"version": "json -f package.json -I -e \"this.repository.sha = '$(git log -n1 --pretty=format:%H)'\""
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "cz-conventional-changelog"
|
||||
}
|
||||
},
|
||||
"tap": {
|
||||
"branches": 60,
|
||||
"functions": 70,
|
||||
"lines": 70,
|
||||
"statements": 70
|
||||
},
|
||||
"dependencies": {
|
||||
"@vernier/godirect": "1.5.0",
|
||||
"arraybuffer-loader": "^1.0.6",
|
||||
@@ -53,6 +65,8 @@
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.13.10",
|
||||
"@babel/preset-env": "7.14.8",
|
||||
"@commitlint/cli": "17.0.2",
|
||||
"@commitlint/config-conventional": "17.0.2",
|
||||
"adm-zip": "0.4.11",
|
||||
"babel-eslint": "10.1.0",
|
||||
"babel-loader": "8.2.2",
|
||||
@@ -65,9 +79,10 @@
|
||||
"file-loader": "2.0.0",
|
||||
"format-message-cli": "6.2.0",
|
||||
"gh-pages": "1.2.0",
|
||||
"husky": "8.0.1",
|
||||
"in-publish": "2.0.1",
|
||||
"jsdoc": "3.6.6",
|
||||
"js-md5": "0.7.3",
|
||||
"jsdoc": "3.6.6",
|
||||
"json": "^9.0.4",
|
||||
"lodash.defaultsdeep": "4.6.1",
|
||||
"pngjs": "3.3.3",
|
||||
@@ -76,11 +91,12 @@
|
||||
"scratch-l10n": "3.14.20220602031631",
|
||||
"scratch-render": "0.1.0-prerelease.20211028200436",
|
||||
"scratch-render-fonts": "1.0.0-prerelease.20210401210003",
|
||||
"scratch-semantic-release-config": "1.0.2",
|
||||
"scratch-storage": "2.0.2",
|
||||
"scratch-svg-renderer": "0.2.0-prerelease.20210727023023",
|
||||
"script-loader": "0.7.2",
|
||||
"stats.js": "0.17.0",
|
||||
"tap": "12.0.1",
|
||||
"tap": "16.2.0",
|
||||
"tiny-worker": "2.3.0",
|
||||
"uglifyjs-webpack-plugin": "1.2.7",
|
||||
"webpack": "4.46.0",
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
module.exports = {
|
||||
extends: 'scratch-semantic-release-config',
|
||||
branches: [
|
||||
{
|
||||
name: 'develop'
|
||||
// default channel
|
||||
},
|
||||
{
|
||||
name: 'hotfix/*',
|
||||
channel: 'hotfix'
|
||||
}
|
||||
]
|
||||
};
|
||||
+10
-1
@@ -608,7 +608,7 @@ class Runtime extends EventEmitter {
|
||||
static get PERIPHERAL_LIST_UPDATE () {
|
||||
return 'PERIPHERAL_LIST_UPDATE';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Event name for when the user picks a bluetooth device to connect to
|
||||
* via Companion Device Manager (CDM)
|
||||
@@ -2578,6 +2578,15 @@ class Runtime extends EventEmitter {
|
||||
this.emit(Runtime.RUNTIME_STARTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Quit the Runtime, clearing any handles which might keep the process alive.
|
||||
* Do not use the runtime after calling this method. This method is meant for test shutdown.
|
||||
*/
|
||||
quit () {
|
||||
clearInterval(this._steppingInterval);
|
||||
this._steppingInterval = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn on profiling.
|
||||
* @param {Profiler/FrameCallback} onFrame A callback handle passed a
|
||||
|
||||
@@ -81,8 +81,8 @@ class ExtensionManager {
|
||||
this.pendingWorkers = [];
|
||||
|
||||
/**
|
||||
* Set of loaded extension URLs/IDs (equivalent for built-in extensions).
|
||||
* @type {Set.<string>}
|
||||
* Map of loaded extension URLs/IDs (equivalent for built-in extensions) to service name.
|
||||
* @type {Map.<string,string>}
|
||||
* @private
|
||||
*/
|
||||
this._loadedExtensions = new Map();
|
||||
|
||||
@@ -231,7 +231,8 @@ class Scratch3VideoSensingBlocks {
|
||||
* @private
|
||||
*/
|
||||
_loop () {
|
||||
setTimeout(this._loop.bind(this), Math.max(this.runtime.currentStepTime, Scratch3VideoSensingBlocks.INTERVAL));
|
||||
const loopTime = Math.max(this.runtime.currentStepTime, Scratch3VideoSensingBlocks.INTERVAL);
|
||||
this._loopInterval = setTimeout(this._loop.bind(this), loopTime);
|
||||
|
||||
// Add frame to detector
|
||||
const time = Date.now();
|
||||
@@ -251,6 +252,13 @@ class Scratch3VideoSensingBlocks {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the video sampling loop. Only used for testing.
|
||||
*/
|
||||
_stopLoop () {
|
||||
clearTimeout(this._loopInterval);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create data for a menu in scratch-blocks format, consisting of an array
|
||||
* of objects with text and value properties. The text is a translated
|
||||
|
||||
@@ -175,6 +175,14 @@ class VirtualMachine extends EventEmitter {
|
||||
this.runtime.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Quit the VM, clearing any handles which might keep the process alive.
|
||||
* Do not use the runtime after calling this method. This method is meant for test shutdown.
|
||||
*/
|
||||
quit () {
|
||||
this.runtime.quit();
|
||||
}
|
||||
|
||||
/**
|
||||
* "Green flag" handler - start all threads starting with a green flag.
|
||||
*/
|
||||
|
||||
Vendored
BIN
Binary file not shown.
@@ -27,8 +27,8 @@ test('default cat', t => {
|
||||
vm.on('playgroundData', e => {
|
||||
const threads = JSON.parse(e.threads);
|
||||
t.ok(threads.length === 0);
|
||||
vm.quit();
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
|
||||
vm.start();
|
||||
|
||||
@@ -33,8 +33,8 @@ test('importing sb2 project where block comment is converted to workspace commen
|
||||
const invalidComments = targetComments.filter(comment => typeof comment.blockId === 'number');
|
||||
t.equal(invalidComments.length, 0);
|
||||
|
||||
vm.quit();
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
|
||||
// Start VM, load project, and run
|
||||
|
||||
@@ -38,8 +38,8 @@ test('importing sb2 project where block comment is converted to workspace commen
|
||||
const targetBlocks = Object.values(target.blocks._blocks);
|
||||
t.equal(targetBlocks.length, 0);
|
||||
|
||||
vm.quit();
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
|
||||
// Start VM, load project, and run
|
||||
|
||||
@@ -62,8 +62,8 @@ test('importing sb2 project with special chars in message names', t => {
|
||||
t.equal(catMessageBlocks[0].fields.BROADCAST_OPTION.id, ltPerfectMessageId);
|
||||
t.equal(catMessageBlocks[1].fields.BROADCAST_OPTION.id, abMessageId);
|
||||
|
||||
vm.quit();
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
|
||||
// Start VM, load project, and run
|
||||
|
||||
@@ -62,8 +62,8 @@ test('importing sb3 project with special chars in message names', t => {
|
||||
t.equal(catMessageBlocks[0].fields.BROADCAST_OPTION.id, ltPerfectMessageId);
|
||||
t.equal(catMessageBlocks[1].fields.BROADCAST_OPTION.id, abMessageId);
|
||||
|
||||
vm.quit();
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
|
||||
// Start VM, load project, and run
|
||||
|
||||
@@ -17,12 +17,6 @@ test('clone-cleanup', t => {
|
||||
*/
|
||||
let testStep = -1;
|
||||
|
||||
/**
|
||||
* We test using setInterval; track the interval ID here so we can cancel it.
|
||||
* @type {object}
|
||||
*/
|
||||
let testInterval = null;
|
||||
|
||||
const verifyCounts = (expectedClones, extraThreads) => {
|
||||
// stage plus one sprite, plus clones
|
||||
t.strictEqual(vm.runtime.targets.length, 2 + expectedClones,
|
||||
@@ -60,17 +54,19 @@ test('clone-cleanup', t => {
|
||||
break;
|
||||
|
||||
case 3:
|
||||
// The second batch of clones has been created and the main thread has ended
|
||||
verifyCounts(10, 0);
|
||||
// The second batch of clones has been created and the main thread is about to end
|
||||
verifyCounts(10, 1);
|
||||
|
||||
// After the main thread ends, do one last test step
|
||||
setTimeout(() => testNextStep(), 1000);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
// The second batch of clones has deleted themselves; everything is finished
|
||||
verifyCounts(0, 0);
|
||||
|
||||
clearInterval(testInterval);
|
||||
vm.quit();
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
break;
|
||||
}
|
||||
};
|
||||
@@ -88,8 +84,8 @@ test('clone-cleanup', t => {
|
||||
|
||||
vm.greenFlag();
|
||||
|
||||
// Every second, advance the testing step
|
||||
testInterval = setInterval(testNextStep, 1000);
|
||||
// Let the project control the pace of the tests
|
||||
vm.runtime.on('SAY', () => testNextStep());
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ test('importing an sb2 project with cloud variables', t => {
|
||||
// when the message is being sent to the server rather than on the client
|
||||
t.equal(variable.isCloud, true);
|
||||
|
||||
vm.quit();
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
@@ -60,6 +61,7 @@ test('importing an sb2 project with cloud variables at the limit for a project',
|
||||
// All of the 8 stage variables should be cloud variables
|
||||
t.equal(stageVars.filter(v => v.isCloud).length, 10);
|
||||
|
||||
vm.quit();
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
@@ -85,6 +87,7 @@ test('importing an sb2 project with cloud variables exceeding the limit for a pr
|
||||
// Only 8 of the variables should have the isCloud flag set to true
|
||||
t.equal(stageVars.filter(v => v.isCloud).length, 10);
|
||||
|
||||
vm.quit();
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
@@ -115,6 +118,7 @@ test('importing one project after the other resets cloud variable limit', t => {
|
||||
|
||||
t.equal(vm.runtime.canAddCloudVariable(), true);
|
||||
|
||||
vm.quit();
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
@@ -145,8 +149,7 @@ test('local cloud variables get imported as regular variables', t => {
|
||||
t.equal(spriteVars.length, 1);
|
||||
t.equal(spriteVars[0].isCloud, false);
|
||||
|
||||
vm.quit();
|
||||
t.end();
|
||||
|
||||
process.nextTick(process.exit); // This is needed because this is the end of the last test in this file!!!
|
||||
});
|
||||
});
|
||||
@@ -35,6 +35,7 @@ test('importing an sb3 project with cloud variables', t => {
|
||||
t.equal(Number(variable.value), 100);
|
||||
t.equal(variable.isCloud, true);
|
||||
|
||||
vm.quit();
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
@@ -58,6 +59,7 @@ test('importing an sb3 project with cloud variables at the limit for a project',
|
||||
// All of the 10 stage variables should be cloud variables
|
||||
t.equal(stageVars.filter(v => v.isCloud).length, 10);
|
||||
|
||||
vm.quit();
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
@@ -83,6 +85,7 @@ test('importing an sb3 project with cloud variables exceeding the limit for a pr
|
||||
// Only 8 of the variables should have the isCloud flag set to true
|
||||
t.equal(stageVars.filter(v => v.isCloud).length, 10);
|
||||
|
||||
vm.quit();
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
@@ -111,6 +114,7 @@ test('importing one project after the other resets cloud variable limit', t => {
|
||||
|
||||
t.equal(vm.runtime.canAddCloudVariable(), true);
|
||||
|
||||
vm.quit();
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
@@ -141,8 +145,7 @@ test('local cloud variables get imported as regular variables', t => {
|
||||
t.equal(spriteVars.length, 1);
|
||||
t.equal(spriteVars[0].isCloud, false);
|
||||
|
||||
vm.quit();
|
||||
t.end();
|
||||
|
||||
process.nextTick(process.exit); // This is needed because this is the end of the last test in this file!!!
|
||||
});
|
||||
});
|
||||
@@ -70,8 +70,8 @@ test('importing sb2 project with comments', t => {
|
||||
t.equal(stopAllBlock.comment, blockComments[4].id);
|
||||
t.equal(stopAllBlock.opcode, 'control_stop');
|
||||
|
||||
vm.quit();
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
|
||||
// Start VM, load project, and run
|
||||
|
||||
@@ -70,8 +70,8 @@ test('load an sb3 project with comments', t => {
|
||||
t.equal(stopAllBlock.comment, blockComments[4].id);
|
||||
t.equal(stopAllBlock.opcode, 'control_stop');
|
||||
|
||||
vm.quit();
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
|
||||
// Start VM, load project, and run
|
||||
|
||||
@@ -19,8 +19,8 @@ test('complex', t => {
|
||||
vm.on('playgroundData', e => {
|
||||
const threads = JSON.parse(e.threads);
|
||||
t.ok(threads.length === 0);
|
||||
vm.quit();
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
|
||||
// Manipulate each target
|
||||
|
||||
@@ -15,8 +15,8 @@ test('control', t => {
|
||||
vm.on('playgroundData', e => {
|
||||
const threads = JSON.parse(e.threads);
|
||||
t.ok(threads.length > 0);
|
||||
vm.quit();
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
|
||||
// Start VM, load project, and run
|
||||
|
||||
@@ -14,8 +14,8 @@ test('data', t => {
|
||||
// Evaluate playground data and exit
|
||||
vm.on('playgroundData', () => {
|
||||
// @todo Additional tests
|
||||
vm.quit();
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
|
||||
// Start VM, load project, and run
|
||||
|
||||
@@ -27,8 +27,8 @@ test('default cat', t => {
|
||||
vm.on('playgroundData', e => {
|
||||
const threads = JSON.parse(e.threads);
|
||||
t.ok(threads.length === 0);
|
||||
vm.quit();
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
|
||||
vm.start();
|
||||
|
||||
@@ -15,8 +15,8 @@ test('event', t => {
|
||||
vm.on('playgroundData', e => {
|
||||
const threads = JSON.parse(e.threads);
|
||||
t.ok(threads.length > 0);
|
||||
vm.quit();
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
|
||||
// Start VM, load project, and run
|
||||
|
||||
@@ -27,7 +27,7 @@ const VirtualMachine = require('../../src/index');
|
||||
* been reached.
|
||||
*/
|
||||
|
||||
const whenThreadsComplete = (t, vm, timeLimit = 2000) => (
|
||||
const whenThreadsComplete = (t, vm, uri, timeLimit = 5000) =>
|
||||
// When the number of threads reaches 0 the test is expected to be complete.
|
||||
new Promise((resolve, reject) => {
|
||||
const intervalId = setInterval(() => {
|
||||
@@ -44,6 +44,7 @@ const whenThreadsComplete = (t, vm, timeLimit = 2000) => (
|
||||
}, 50);
|
||||
|
||||
const timeoutId = setTimeout(() => {
|
||||
t.fail(`Timeout waiting for threads to complete: ${uri}`);
|
||||
reject(new Error('time limit reached'));
|
||||
}, timeLimit);
|
||||
|
||||
@@ -53,8 +54,7 @@ const whenThreadsComplete = (t, vm, timeLimit = 2000) => (
|
||||
clearInterval(intervalId);
|
||||
clearTimeout(timeoutId);
|
||||
});
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
const executeDir = path.resolve(__dirname, '../fixtures/execute');
|
||||
|
||||
@@ -66,6 +66,8 @@ fs.readdirSync(executeDir)
|
||||
log.suggest.deny('vm', 'error');
|
||||
t.tearDown(() => log.suggest.clear());
|
||||
|
||||
const vm = new VirtualMachine();
|
||||
|
||||
// Map string messages to tap reporting methods. This will be used
|
||||
// with events from scratch's runtime emitted on block instructions.
|
||||
let didPlan;
|
||||
@@ -86,6 +88,7 @@ fs.readdirSync(executeDir)
|
||||
},
|
||||
end () {
|
||||
didEnd = true;
|
||||
vm.quit();
|
||||
t.end();
|
||||
}
|
||||
};
|
||||
@@ -100,7 +103,6 @@ fs.readdirSync(executeDir)
|
||||
return reporters.comment(text);
|
||||
};
|
||||
|
||||
const vm = new VirtualMachine();
|
||||
vm.attachStorage(makeTestStorage());
|
||||
|
||||
// Start the VM and initialize some vm properties.
|
||||
@@ -125,7 +127,7 @@ fs.readdirSync(executeDir)
|
||||
// the scratch project sent us a "end" message.
|
||||
return vm.loadProject(project)
|
||||
.then(() => vm.greenFlag())
|
||||
.then(() => whenThreadsComplete(t, vm))
|
||||
.then(() => whenThreadsComplete(t, vm, uri))
|
||||
.then(() => {
|
||||
// Setting a plan is not required but is a good idea.
|
||||
if (!didPlan) {
|
||||
@@ -138,6 +140,7 @@ fs.readdirSync(executeDir)
|
||||
// it can be resolved.
|
||||
if (!didEnd) {
|
||||
t.fail('did not say "end"');
|
||||
vm.quit();
|
||||
t.end();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -20,8 +20,8 @@ test('complex', t => {
|
||||
const results = vm.runtime.targets[0].variables[resultKey].value;
|
||||
t.deepEqual(results, ['3', '2', '1', 'stage']);
|
||||
|
||||
vm.quit();
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
|
||||
// Start VM, load project, and run
|
||||
|
||||
@@ -15,8 +15,8 @@ test('default', t => {
|
||||
vm.on('playgroundData', e => {
|
||||
const threads = JSON.parse(e.threads);
|
||||
t.ok(threads.length === 0);
|
||||
vm.quit();
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
|
||||
// Start VM, load project, and run
|
||||
|
||||
@@ -15,8 +15,8 @@ test('default', t => {
|
||||
vm.on('playgroundData', e => {
|
||||
const threads = JSON.parse(e.threads);
|
||||
t.ok(threads.length === 0);
|
||||
vm.quit();
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
|
||||
// Start VM, load project, and run
|
||||
|
||||
@@ -31,8 +31,8 @@ test('importing sb3 project with incorrect list monitor name', t => {
|
||||
t.equal(monitorBlock.fields.LIST.value, renamedListName);
|
||||
}
|
||||
|
||||
vm.quit();
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
|
||||
// Start VM, load project, and run
|
||||
|
||||
@@ -3,9 +3,19 @@ const tap = require('tap');
|
||||
const {test} = tap;
|
||||
const fs = require('fs');
|
||||
const readFileToBuffer = require('../fixtures/readProjectFile').readFileToBuffer;
|
||||
const dispatch = require('../../src/dispatch/central-dispatch');
|
||||
const VirtualMachine = require('../../src/index');
|
||||
|
||||
tap.tearDown(() => process.nextTick(process.exit));
|
||||
/**
|
||||
* Call _stopLoop() on the Video Sensing extension.
|
||||
* @param {VirtualMachine} vm - a VM instance which has loaded the 'videoSensing' extension.
|
||||
*/
|
||||
const stopVideoLoop = vm => {
|
||||
// TODO: provide a general way to tell extensions to shut down
|
||||
// Ideally we'd just dispose of the extension's Worker...
|
||||
const serviceName = vm.extensionManager._loadedExtensions.get('videoSensing');
|
||||
dispatch.call(serviceName, '_stopLoop');
|
||||
};
|
||||
|
||||
test('Load external extensions', async t => {
|
||||
const vm = new VirtualMachine();
|
||||
@@ -25,6 +35,9 @@ test('Load external extensions', async t => {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
stopVideoLoop(vm);
|
||||
vm.quit();
|
||||
t.end();
|
||||
});
|
||||
|
||||
@@ -64,5 +77,7 @@ test('Load video sensing extension and video properties', async t => {
|
||||
t.equal(vm.runtime.ioDevices.video._ghost, project.videoTransparency);
|
||||
}
|
||||
|
||||
stopVideoLoop(vm);
|
||||
vm.quit();
|
||||
t.end();
|
||||
});
|
||||
@@ -17,8 +17,8 @@ test('sb2 project (originally from Scratch 1.4) with missing backdrop image shou
|
||||
vm.on('playgroundData', e => {
|
||||
const threads = JSON.parse(e.threads);
|
||||
t.ok(threads.length === 0);
|
||||
vm.quit();
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
|
||||
vm.start();
|
||||
|
||||
@@ -15,8 +15,8 @@ test('looks', t => {
|
||||
vm.on('playgroundData', e => {
|
||||
const threads = JSON.parse(e.threads);
|
||||
t.ok(threads.length === 0);
|
||||
vm.quit();
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
|
||||
// Start VM, load project, and run
|
||||
|
||||
@@ -122,8 +122,8 @@ test('importing sb2 project with monitors', t => {
|
||||
t.equal(monitorRecord.spriteName, null);
|
||||
t.equal(monitorRecord.targetId, null);
|
||||
|
||||
vm.quit();
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
|
||||
// Start VM, load project, and run
|
||||
|
||||
@@ -140,7 +140,6 @@ test('saving and loading sb2 project with monitors preserves sliderMin and slide
|
||||
t.equal(monitorRecord.targetId, null);
|
||||
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
|
||||
// Start VM, load project, and run
|
||||
|
||||
@@ -247,8 +247,8 @@ test('importing sb3 project with monitors', t => {
|
||||
t.equal(monitorRecord.targetId, null);
|
||||
t.equal(vm.extensionManager.isExtensionLoaded('ev3'), true);
|
||||
|
||||
vm.quit();
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
|
||||
// Start VM, load project, and run
|
||||
|
||||
@@ -15,8 +15,8 @@ test('motion', t => {
|
||||
vm.on('playgroundData', e => {
|
||||
const threads = JSON.parse(e.threads);
|
||||
t.ok(threads.length > 0);
|
||||
vm.quit();
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
|
||||
// Start VM, load project, and run
|
||||
|
||||
@@ -33,8 +33,8 @@ test('offline-custom-assets', t => {
|
||||
vm.on('playgroundData', e => {
|
||||
const threads = JSON.parse(e.threads);
|
||||
t.ok(threads.length === 0);
|
||||
vm.quit();
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
|
||||
// Start VM, load project, and run
|
||||
|
||||
@@ -37,8 +37,8 @@ test('pen', t => {
|
||||
t.equal(originalPenState.penAttributes.diameter, 51);
|
||||
t.equal(clonePenState.penAttributes.diameter, 42);
|
||||
|
||||
vm.quit();
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
|
||||
// Start VM, load project, and run
|
||||
|
||||
@@ -15,8 +15,8 @@ test('procedure', t => {
|
||||
vm.on('playgroundData', e => {
|
||||
const threads = JSON.parse(e.threads);
|
||||
t.ok(threads.length === 0);
|
||||
vm.quit();
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
|
||||
// Start VM, load project, and run
|
||||
|
||||
@@ -19,8 +19,8 @@ test('Running project should not emit project changed event', t => {
|
||||
// Evaluate playground data and exit
|
||||
vm.on('playgroundData', () => {
|
||||
t.equal(projectChanged, false);
|
||||
vm.quit();
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
|
||||
// Start VM, load project, and run
|
||||
|
||||
@@ -29,8 +29,8 @@ test('say/think and wait', t => {
|
||||
// The test will fail if the project throws.
|
||||
setTimeout(() => {
|
||||
vm.stopAll();
|
||||
vm.quit();
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
}, 2000);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -25,8 +25,6 @@ const visibleTempoMonitorProjectUri = path.resolve(
|
||||
__dirname, '../fixtures/visible-tempo-monitor-no-other-music-blocks.sb2');
|
||||
const visibleTempoMonitorProject = readFileToBuffer(visibleTempoMonitorProjectUri);
|
||||
|
||||
tap.tearDown(() => process.nextTick(process.exit));
|
||||
|
||||
test('loading sb2 project with invisible video monitor should not load monitor or extension', t => {
|
||||
const vm = new VirtualMachine();
|
||||
vm.attachStorage(makeTestStorage());
|
||||
@@ -39,6 +37,7 @@ test('loading sb2 project with invisible video monitor should not load monitor o
|
||||
vm.loadProject(invisibleVideoMonitorProject).then(() => {
|
||||
t.equal(vm.extensionManager.isExtensionLoaded('videoSensing'), false);
|
||||
t.equal(vm.runtime._monitorState.size, 0);
|
||||
vm.quit();
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
@@ -55,6 +54,7 @@ test('loading sb2 project with visible video monitor should not load extension',
|
||||
vm.loadProject(visibleVideoMonitorProject).then(() => {
|
||||
t.equal(vm.extensionManager.isExtensionLoaded('videoSensing'), false);
|
||||
t.equal(vm.runtime._monitorState.size, 0);
|
||||
vm.quit();
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
@@ -86,6 +86,7 @@ test('sb2 project with invisible music monitor should not load monitor or extens
|
||||
vm.loadProject(invisibleTempoMonitorProject).then(() => {
|
||||
t.equal(vm.extensionManager.isExtensionLoaded('music'), false);
|
||||
t.equal(vm.runtime._monitorState.size, 0);
|
||||
vm.quit();
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
@@ -104,6 +105,7 @@ test('sb2 project with visible music monitor should load monitor and extension',
|
||||
t.equal(vm.runtime._monitorState.size, 1);
|
||||
t.equal(vm.runtime._monitorState.has('music_getTempo'), true);
|
||||
t.equal(vm.runtime._monitorState.get('music_getTempo').visible, true);
|
||||
vm.quit();
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
@@ -71,14 +71,14 @@ const test = tap.test;
|
||||
|
||||
test('load sb2 project with corrupted bitmap costume file', t => {
|
||||
t.equal(vm.runtime.targets.length, 2);
|
||||
|
||||
|
||||
const stage = vm.runtime.targets[0];
|
||||
t.ok(stage.isStage);
|
||||
|
||||
const greenGuySprite = vm.runtime.targets[1];
|
||||
t.equal(greenGuySprite.getName(), 'GreenGuy');
|
||||
t.equal(greenGuySprite.getCostumes().length, 1);
|
||||
|
||||
|
||||
const corruptedCostume = greenGuySprite.getCostumes()[0];
|
||||
t.equal(corruptedCostume.name, 'GreenGuy');
|
||||
t.equal(corruptedCostume.assetId, defaultBitmapAssetId);
|
||||
@@ -96,14 +96,14 @@ test('load and then save project with corrupted bitmap costume file', t => {
|
||||
const resavedProject = JSON.parse(vm.toJSON());
|
||||
|
||||
t.equal(resavedProject.targets.length, 2);
|
||||
|
||||
|
||||
const stage = resavedProject.targets[0];
|
||||
t.ok(stage.isStage);
|
||||
|
||||
const greenGuySprite = resavedProject.targets[1];
|
||||
t.equal(greenGuySprite.name, 'GreenGuy');
|
||||
t.equal(greenGuySprite.costumes.length, 1);
|
||||
|
||||
|
||||
const corruptedCostume = greenGuySprite.costumes[0];
|
||||
t.equal(corruptedCostume.name, 'GreenGuy');
|
||||
// Resaved project costume should have the metadata that corresponds to the original broken costume
|
||||
@@ -122,5 +122,4 @@ test('serializeCostume saves orignal broken costume', t => {
|
||||
t.equal(costume.fileName, `${brokenCostumeMd5}.png`);
|
||||
t.equal(md5(costume.fileContent), brokenCostumeMd5);
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
@@ -30,7 +30,7 @@ global.Image = function () {
|
||||
width: 1,
|
||||
height: 1
|
||||
};
|
||||
|
||||
|
||||
setTimeout(() => image.onload(), 1000);
|
||||
return image;
|
||||
};
|
||||
@@ -73,14 +73,14 @@ const test = tap.test;
|
||||
|
||||
test('load sb2 project with corrupted vector costume file', t => {
|
||||
t.equal(vm.runtime.targets.length, 2);
|
||||
|
||||
|
||||
const stage = vm.runtime.targets[0];
|
||||
t.ok(stage.isStage);
|
||||
|
||||
const blueGuySprite = vm.runtime.targets[1];
|
||||
t.equal(blueGuySprite.getName(), 'Blue Guy');
|
||||
t.equal(blueGuySprite.getCostumes().length, 1);
|
||||
|
||||
|
||||
const corruptedCostume = blueGuySprite.getCostumes()[0];
|
||||
t.equal(corruptedCostume.name, 'Blue Guy 2');
|
||||
t.equal(corruptedCostume.assetId, defaultVectorAssetId);
|
||||
@@ -98,14 +98,14 @@ test('load and then save project with corrupted vector costume file', t => {
|
||||
const resavedProject = JSON.parse(vm.toJSON());
|
||||
|
||||
t.equal(resavedProject.targets.length, 2);
|
||||
|
||||
|
||||
const stage = resavedProject.targets[0];
|
||||
t.ok(stage.isStage);
|
||||
|
||||
const blueGuySprite = resavedProject.targets[1];
|
||||
t.equal(blueGuySprite.name, 'Blue Guy');
|
||||
t.equal(blueGuySprite.costumes.length, 1);
|
||||
|
||||
|
||||
const corruptedCostume = blueGuySprite.costumes[0];
|
||||
t.equal(corruptedCostume.name, 'Blue Guy 2');
|
||||
// Resaved project costume should have the metadata that corresponds to the original broken costume
|
||||
@@ -124,5 +124,4 @@ test('serializeCostume saves orignal broken costume', t => {
|
||||
t.equal(costume.fileName, `${brokenCostumeMd5}.svg`);
|
||||
t.equal(md5(costume.fileContent), brokenCostumeMd5);
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
@@ -56,14 +56,14 @@ const test = tap.test;
|
||||
|
||||
test('loading sb2 project with missing bitmap costume file', t => {
|
||||
t.equal(vm.runtime.targets.length, 2);
|
||||
|
||||
|
||||
const stage = vm.runtime.targets[0];
|
||||
t.ok(stage.isStage);
|
||||
|
||||
const greenGuySprite = vm.runtime.targets[1];
|
||||
t.equal(greenGuySprite.getName(), 'GreenGuy');
|
||||
t.equal(greenGuySprite.getCostumes().length, 1);
|
||||
|
||||
|
||||
const missingCostume = greenGuySprite.getCostumes()[0];
|
||||
t.equal(missingCostume.name, 'GreenGuy');
|
||||
// Costume should have both default cosutme (e.g. Gray Question Mark) data and original data
|
||||
@@ -81,14 +81,14 @@ test('load and then save sb2 project with missing costume file', t => {
|
||||
const resavedProject = JSON.parse(vm.toJSON());
|
||||
|
||||
t.equal(resavedProject.targets.length, 2);
|
||||
|
||||
|
||||
const stage = resavedProject.targets[0];
|
||||
t.ok(stage.isStage);
|
||||
|
||||
const greenGuySprite = resavedProject.targets[1];
|
||||
t.equal(greenGuySprite.name, 'GreenGuy');
|
||||
t.equal(greenGuySprite.costumes.length, 1);
|
||||
|
||||
|
||||
const missingCostume = greenGuySprite.costumes[0];
|
||||
t.equal(missingCostume.name, 'GreenGuy');
|
||||
// Costume should have both default cosutme (e.g. Gray Question Mark) data and original data
|
||||
@@ -102,10 +102,9 @@ test('load and then save sb2 project with missing costume file', t => {
|
||||
|
||||
test('serializeCostume does not save data for missing costume', t => {
|
||||
const costumeDescs = serializeCostumes(vm.runtime);
|
||||
|
||||
|
||||
t.equal(costumeDescs.length, 1); // Should only have one costume, the backdrop
|
||||
t.not(costumeDescs[0].fileName, `${missingCostumeAssetId}.png`);
|
||||
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
@@ -55,14 +55,14 @@ const test = tap.test;
|
||||
|
||||
test('loading sb2 project with missing vector costume file', t => {
|
||||
t.equal(vm.runtime.targets.length, 2);
|
||||
|
||||
|
||||
const stage = vm.runtime.targets[0];
|
||||
t.ok(stage.isStage);
|
||||
|
||||
const blueGuySprite = vm.runtime.targets[1];
|
||||
t.equal(blueGuySprite.getName(), 'Blue Guy');
|
||||
t.equal(blueGuySprite.getCostumes().length, 1);
|
||||
|
||||
|
||||
const missingCostume = blueGuySprite.getCostumes()[0];
|
||||
t.equal(missingCostume.name, 'Blue Guy 2');
|
||||
// Costume should have both default cosutme (e.g. Gray Question Mark) data and original data
|
||||
@@ -80,14 +80,14 @@ test('load and then save sb2 project with missing costume file', t => {
|
||||
const resavedProject = JSON.parse(vm.toJSON());
|
||||
|
||||
t.equal(resavedProject.targets.length, 2);
|
||||
|
||||
|
||||
const stage = resavedProject.targets[0];
|
||||
t.ok(stage.isStage);
|
||||
|
||||
const blueGuySprite = resavedProject.targets[1];
|
||||
t.equal(blueGuySprite.name, 'Blue Guy');
|
||||
t.equal(blueGuySprite.costumes.length, 1);
|
||||
|
||||
|
||||
const missingCostume = blueGuySprite.costumes[0];
|
||||
t.equal(missingCostume.name, 'Blue Guy 2');
|
||||
// Costume should have both default cosutme (e.g. Gray Question Mark) data and original data
|
||||
@@ -101,10 +101,9 @@ test('load and then save sb2 project with missing costume file', t => {
|
||||
|
||||
test('serializeCostume does not save data for missing costume', t => {
|
||||
const costumeDescs = serializeCostumes(vm.runtime);
|
||||
|
||||
|
||||
t.equal(costumeDescs.length, 1); // Should only have one costume, the backdrop
|
||||
t.not(costumeDescs[0].fileName, `${missingCostumeAssetId}.svg`);
|
||||
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
@@ -71,14 +71,14 @@ const test = tap.test;
|
||||
|
||||
test('load sb3 project with corrupted bitmap costume file', t => {
|
||||
t.equal(vm.runtime.targets.length, 2);
|
||||
|
||||
|
||||
const stage = vm.runtime.targets[0];
|
||||
t.ok(stage.isStage);
|
||||
|
||||
const greenGuySprite = vm.runtime.targets[1];
|
||||
t.equal(greenGuySprite.getName(), 'Green Guy');
|
||||
t.equal(greenGuySprite.getCostumes().length, 1);
|
||||
|
||||
|
||||
const corruptedCostume = greenGuySprite.getCostumes()[0];
|
||||
t.equal(corruptedCostume.name, 'Green Guy');
|
||||
t.equal(corruptedCostume.assetId, defaultBitmapAssetId);
|
||||
@@ -96,14 +96,14 @@ test('load and then save project with corrupted bitmap costume file', t => {
|
||||
const resavedProject = JSON.parse(vm.toJSON());
|
||||
|
||||
t.equal(resavedProject.targets.length, 2);
|
||||
|
||||
|
||||
const stage = resavedProject.targets[0];
|
||||
t.ok(stage.isStage);
|
||||
|
||||
const greenGuySprite = resavedProject.targets[1];
|
||||
t.equal(greenGuySprite.name, 'Green Guy');
|
||||
t.equal(greenGuySprite.costumes.length, 1);
|
||||
|
||||
|
||||
const corruptedCostume = greenGuySprite.costumes[0];
|
||||
t.equal(corruptedCostume.name, 'Green Guy');
|
||||
// Resaved project costume should have the metadata that corresponds to the original broken costume
|
||||
@@ -122,5 +122,4 @@ test('serializeCostume saves orignal broken costume', t => {
|
||||
t.equal(costume.fileName, `${brokenCostumeMd5}.png`);
|
||||
t.equal(md5(costume.fileContent), brokenCostumeMd5);
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
@@ -32,7 +32,7 @@ const FakeAudioEngine = function () {
|
||||
if (soundDataString.includes('here is some')) {
|
||||
return Promise.reject(new Error('mock audio engine broke'));
|
||||
}
|
||||
|
||||
|
||||
// Otherwise return fake data
|
||||
return Promise.resolve({
|
||||
id: fakeId++,
|
||||
@@ -65,14 +65,14 @@ const test = tap.test;
|
||||
|
||||
test('load sb3 project with corrupted sound file', t => {
|
||||
t.equal(vm.runtime.targets.length, 2);
|
||||
|
||||
|
||||
const stage = vm.runtime.targets[0];
|
||||
t.ok(stage.isStage);
|
||||
|
||||
const catSprite = vm.runtime.targets[1];
|
||||
t.equal(catSprite.getName(), 'Sprite1');
|
||||
t.equal(catSprite.getSounds().length, 1);
|
||||
|
||||
|
||||
const corruptedSound = catSprite.getSounds()[0];
|
||||
t.equal(corruptedSound.name, 'Boop Sound Recording');
|
||||
t.equal(corruptedSound.assetId, defaultSoundAssetId);
|
||||
@@ -90,14 +90,14 @@ test('load and then save project with corrupted sound file', t => {
|
||||
const resavedProject = JSON.parse(vm.toJSON());
|
||||
|
||||
t.equal(resavedProject.targets.length, 2);
|
||||
|
||||
|
||||
const stage = resavedProject.targets[0];
|
||||
t.ok(stage.isStage);
|
||||
|
||||
const catSprite = resavedProject.targets[1];
|
||||
t.equal(catSprite.name, 'Sprite1');
|
||||
t.equal(catSprite.sounds.length, 1);
|
||||
|
||||
|
||||
const corruptedSound = catSprite.sounds[0];
|
||||
t.equal(corruptedSound.name, 'Boop Sound Recording');
|
||||
// Resaved project costume should have the metadata that corresponds to the original broken costume
|
||||
@@ -116,5 +116,4 @@ test('serializeSounds saves orignal broken sound', t => {
|
||||
t.equal(sound.fileName, `${brokenSoundMd5}.wav`);
|
||||
t.equal(md5(sound.fileContent), brokenSoundMd5);
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
@@ -52,14 +52,14 @@ const test = tap.test;
|
||||
|
||||
test('load sb3 project with corrupted vector costume file', t => {
|
||||
t.equal(vm.runtime.targets.length, 2);
|
||||
|
||||
|
||||
const stage = vm.runtime.targets[0];
|
||||
t.ok(stage.isStage);
|
||||
|
||||
const blueGuySprite = vm.runtime.targets[1];
|
||||
t.equal(blueGuySprite.getName(), 'Blue Square Guy');
|
||||
t.equal(blueGuySprite.getCostumes().length, 1);
|
||||
|
||||
|
||||
const corruptedCostume = blueGuySprite.getCostumes()[0];
|
||||
t.equal(corruptedCostume.name, 'costume1');
|
||||
t.equal(corruptedCostume.assetId, defaultVectorAssetId);
|
||||
@@ -77,14 +77,14 @@ test('load and then save project with corrupted vector costume file', t => {
|
||||
const resavedProject = JSON.parse(vm.toJSON());
|
||||
|
||||
t.equal(resavedProject.targets.length, 2);
|
||||
|
||||
|
||||
const stage = resavedProject.targets[0];
|
||||
t.ok(stage.isStage);
|
||||
|
||||
const blueGuySprite = resavedProject.targets[1];
|
||||
t.equal(blueGuySprite.name, 'Blue Square Guy');
|
||||
t.equal(blueGuySprite.costumes.length, 1);
|
||||
|
||||
|
||||
const corruptedCostume = blueGuySprite.costumes[0];
|
||||
t.equal(corruptedCostume.name, 'costume1');
|
||||
// Resaved project costume should have the metadata that corresponds to the original broken costume
|
||||
@@ -103,5 +103,4 @@ test('serializeCostume saves orignal broken costume', t => {
|
||||
t.equal(costume.fileName, `${brokenCostumeMd5}.svg`);
|
||||
t.equal(md5(costume.fileContent), brokenCostumeMd5);
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
@@ -56,14 +56,14 @@ const test = tap.test;
|
||||
|
||||
test('loading sb3 project with missing bitmap costume file', t => {
|
||||
t.equal(vm.runtime.targets.length, 2);
|
||||
|
||||
|
||||
const stage = vm.runtime.targets[0];
|
||||
t.ok(stage.isStage);
|
||||
|
||||
const greenGuySprite = vm.runtime.targets[1];
|
||||
t.equal(greenGuySprite.getName(), 'Green Guy');
|
||||
t.equal(greenGuySprite.getCostumes().length, 1);
|
||||
|
||||
|
||||
const missingCostume = greenGuySprite.getCostumes()[0];
|
||||
t.equal(missingCostume.name, 'Green Guy');
|
||||
// Costume should have both default cosutme (e.g. Gray Question Mark) data and original data
|
||||
@@ -81,14 +81,14 @@ test('load and then save sb3 project with missing costume file', t => {
|
||||
const resavedProject = JSON.parse(vm.toJSON());
|
||||
|
||||
t.equal(resavedProject.targets.length, 2);
|
||||
|
||||
|
||||
const stage = resavedProject.targets[0];
|
||||
t.ok(stage.isStage);
|
||||
|
||||
const greenGuySprite = resavedProject.targets[1];
|
||||
t.equal(greenGuySprite.name, 'Green Guy');
|
||||
t.equal(greenGuySprite.costumes.length, 1);
|
||||
|
||||
|
||||
const missingCostume = greenGuySprite.costumes[0];
|
||||
t.equal(missingCostume.name, 'Green Guy');
|
||||
// Costume should have both default cosutme (e.g. Gray Question Mark) data and original data
|
||||
@@ -102,10 +102,9 @@ test('load and then save sb3 project with missing costume file', t => {
|
||||
|
||||
test('serializeCostume does not save data for missing costume', t => {
|
||||
const costumeDescs = serializeCostumes(vm.runtime);
|
||||
|
||||
|
||||
t.equal(costumeDescs.length, 1); // Should only have one costume, the backdrop
|
||||
t.not(costumeDescs[0].fileName, `${missingCostumeAssetId}.png`);
|
||||
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
@@ -32,13 +32,13 @@ const test = tap.test;
|
||||
|
||||
test('loading sb3 project with missing sound file', t => {
|
||||
t.equal(vm.runtime.targets.length, 2);
|
||||
|
||||
|
||||
const stage = vm.runtime.targets[0];
|
||||
t.ok(stage.isStage);
|
||||
|
||||
const catSprite = vm.runtime.targets[1];
|
||||
t.equal(catSprite.getSounds().length, 1);
|
||||
|
||||
|
||||
const missingSound = catSprite.getSounds()[0];
|
||||
t.equal(missingSound.name, 'Boop Sound Recording');
|
||||
// Sound should have original data but no asset
|
||||
@@ -57,14 +57,14 @@ test('load and then save sb3 project with missing sound file', t => {
|
||||
const resavedProject = JSON.parse(vm.toJSON());
|
||||
|
||||
t.equal(resavedProject.targets.length, 2);
|
||||
|
||||
|
||||
const stage = resavedProject.targets[0];
|
||||
t.ok(stage.isStage);
|
||||
|
||||
const catSprite = resavedProject.targets[1];
|
||||
t.equal(catSprite.name, 'Sprite1');
|
||||
t.equal(catSprite.sounds.length, 1);
|
||||
|
||||
|
||||
const missingSound = catSprite.sounds[0];
|
||||
t.equal(missingSound.name, 'Boop Sound Recording');
|
||||
// Costume should have both default sound data (e.g. "Gray Question Sound" ^_^) and original data
|
||||
@@ -78,10 +78,9 @@ test('load and then save sb3 project with missing sound file', t => {
|
||||
|
||||
test('serializeCostume does not save data for missing costume', t => {
|
||||
const soundDescs = serializeSounds(vm.runtime);
|
||||
|
||||
|
||||
t.equal(soundDescs.length, 1); // Should only have one sound, the pop sound for the stage
|
||||
t.not(soundDescs[0].fileName, `${missingSoundAssetId}.wav`);
|
||||
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
@@ -35,14 +35,14 @@ const test = tap.test;
|
||||
|
||||
test('loading sb3 project with missing vector costume file', t => {
|
||||
t.equal(vm.runtime.targets.length, 2);
|
||||
|
||||
|
||||
const stage = vm.runtime.targets[0];
|
||||
t.ok(stage.isStage);
|
||||
|
||||
const blueGuySprite = vm.runtime.targets[1];
|
||||
t.equal(blueGuySprite.getName(), 'Blue Square Guy');
|
||||
t.equal(blueGuySprite.getCostumes().length, 1);
|
||||
|
||||
|
||||
const missingCostume = blueGuySprite.getCostumes()[0];
|
||||
t.equal(missingCostume.name, 'costume1');
|
||||
// Costume should have both default cosutme (e.g. Gray Question Mark) data and original data
|
||||
@@ -60,14 +60,14 @@ test('load and then save sb3 project with missing costume file', t => {
|
||||
const resavedProject = JSON.parse(vm.toJSON());
|
||||
|
||||
t.equal(resavedProject.targets.length, 2);
|
||||
|
||||
|
||||
const stage = resavedProject.targets[0];
|
||||
t.ok(stage.isStage);
|
||||
|
||||
const blueGuySprite = resavedProject.targets[1];
|
||||
t.equal(blueGuySprite.name, 'Blue Square Guy');
|
||||
t.equal(blueGuySprite.costumes.length, 1);
|
||||
|
||||
|
||||
const missingCostume = blueGuySprite.costumes[0];
|
||||
t.equal(missingCostume.name, 'costume1');
|
||||
// Costume should have both default cosutme (e.g. Gray Question Mark) data and original data
|
||||
@@ -81,10 +81,9 @@ test('load and then save sb3 project with missing costume file', t => {
|
||||
|
||||
test('serializeCostume does not save data for missing costume', t => {
|
||||
const costumeDescs = serializeCostumes(vm.runtime);
|
||||
|
||||
|
||||
t.equal(costumeDescs.length, 1); // Should only have one costume, the backdrop
|
||||
t.not(costumeDescs[0].fileName, `${missingCostumeAssetId}.svg`);
|
||||
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
@@ -15,8 +15,8 @@ test('sensing', t => {
|
||||
vm.on('playgroundData', e => {
|
||||
const threads = JSON.parse(e.threads);
|
||||
t.ok(threads.length > 0);
|
||||
vm.quit();
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
|
||||
// Start VM, load project, and run
|
||||
|
||||
@@ -20,8 +20,8 @@ test('sound', t => {
|
||||
vm.on('playgroundData', e => {
|
||||
const threads = JSON.parse(e.threads);
|
||||
t.ok(threads.length > 0);
|
||||
vm.quit();
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
|
||||
// Start VM, load project, and run
|
||||
|
||||
@@ -76,14 +76,14 @@ const test = tap.test;
|
||||
|
||||
test('load sprite2 with corrupted bitmap costume file', t => {
|
||||
t.equal(vm.runtime.targets.length, 3);
|
||||
|
||||
|
||||
const stage = vm.runtime.targets[0];
|
||||
t.ok(stage.isStage);
|
||||
|
||||
const greenGuySprite = vm.runtime.targets[2];
|
||||
t.equal(greenGuySprite.getName(), 'GreenGuy');
|
||||
t.equal(greenGuySprite.getCostumes().length, 1);
|
||||
|
||||
|
||||
const corruptedCostume = greenGuySprite.getCostumes()[0];
|
||||
t.equal(corruptedCostume.name, 'GreenGuy');
|
||||
t.equal(corruptedCostume.assetId, defaultBitmapAssetId);
|
||||
@@ -102,7 +102,7 @@ test('load and then save sprite with corrupted costume file', t => {
|
||||
|
||||
t.equal(resavedSprite.name, 'GreenGuy');
|
||||
t.equal(resavedSprite.costumes.length, 1);
|
||||
|
||||
|
||||
const corruptedCostume = resavedSprite.costumes[0];
|
||||
t.equal(corruptedCostume.name, 'GreenGuy');
|
||||
// Resaved project costume should have the metadata that corresponds to the original broken costume
|
||||
@@ -121,5 +121,4 @@ test('serializeCostume saves orignal broken costume', t => {
|
||||
t.equal(costume.fileName, `${brokenCostumeMd5}.png`);
|
||||
t.equal(md5(costume.fileContent), brokenCostumeMd5);
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
@@ -77,14 +77,14 @@ const test = tap.test;
|
||||
|
||||
test('load sprite2 with corrupted vector costume file', t => {
|
||||
t.equal(vm.runtime.targets.length, 3);
|
||||
|
||||
|
||||
const stage = vm.runtime.targets[0];
|
||||
t.ok(stage.isStage);
|
||||
|
||||
const blueGuySprite = vm.runtime.targets[2];
|
||||
t.equal(blueGuySprite.getName(), 'Blue Guy');
|
||||
t.equal(blueGuySprite.getCostumes().length, 1);
|
||||
|
||||
|
||||
const corruptedCostume = blueGuySprite.getCostumes()[0];
|
||||
t.equal(corruptedCostume.name, 'Blue Guy 2');
|
||||
t.equal(corruptedCostume.assetId, defaultVectorAssetId);
|
||||
@@ -103,7 +103,7 @@ test('load and then save sprite with corrupted costume file', t => {
|
||||
|
||||
t.equal(resavedSprite.name, 'Blue Guy');
|
||||
t.equal(resavedSprite.costumes.length, 1);
|
||||
|
||||
|
||||
const corruptedCostume = resavedSprite.costumes[0];
|
||||
t.equal(corruptedCostume.name, 'Blue Guy 2');
|
||||
// Resaved project costume should have the metadata that corresponds to the original broken costume
|
||||
@@ -122,5 +122,4 @@ test('serializeCostume saves orignal broken costume', t => {
|
||||
t.equal(costume.fileName, `${brokenCostumeMd5}.svg`);
|
||||
t.equal(md5(costume.fileContent), brokenCostumeMd5);
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
@@ -59,14 +59,14 @@ const test = tap.test;
|
||||
|
||||
test('loading sprite2 with missing bitmap costume file', t => {
|
||||
t.equal(vm.runtime.targets.length, 3);
|
||||
|
||||
|
||||
const stage = vm.runtime.targets[0];
|
||||
t.ok(stage.isStage);
|
||||
|
||||
const greenGuySprite = vm.runtime.targets[2];
|
||||
t.equal(greenGuySprite.getName(), 'GreenGuy');
|
||||
t.equal(greenGuySprite.getCostumes().length, 1);
|
||||
|
||||
|
||||
const missingCostume = greenGuySprite.getCostumes()[0];
|
||||
t.equal(missingCostume.name, 'GreenGuy');
|
||||
// Costume should have both default cosutme (e.g. Gray Question Mark) data and original data
|
||||
@@ -85,7 +85,7 @@ test('load and then save sprite2 with missing bitmap costume file', t => {
|
||||
|
||||
t.equal(resavedSprite.name, 'GreenGuy');
|
||||
t.equal(resavedSprite.costumes.length, 1);
|
||||
|
||||
|
||||
const missingCostume = resavedSprite.costumes[0];
|
||||
t.equal(missingCostume.name, 'GreenGuy');
|
||||
// Costume should have both default cosutme (e.g. Gray Question Mark) data and original data
|
||||
@@ -99,9 +99,8 @@ test('load and then save sprite2 with missing bitmap costume file', t => {
|
||||
|
||||
test('serializeCostume does not save data for missing costume', t => {
|
||||
const costumeDescs = serializeCostumes(vm.runtime, vm.runtime.targets[2].id);
|
||||
|
||||
|
||||
t.equal(costumeDescs.length, 0);
|
||||
|
||||
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
@@ -59,14 +59,14 @@ const test = tap.test;
|
||||
|
||||
test('loading sprite2 with missing vector costume file', t => {
|
||||
t.equal(vm.runtime.targets.length, 3);
|
||||
|
||||
|
||||
const stage = vm.runtime.targets[0];
|
||||
t.ok(stage.isStage);
|
||||
|
||||
const blueGuySprite = vm.runtime.targets[2];
|
||||
t.equal(blueGuySprite.getName(), 'Blue Guy');
|
||||
t.equal(blueGuySprite.getCostumes().length, 1);
|
||||
|
||||
|
||||
const missingCostume = blueGuySprite.getCostumes()[0];
|
||||
t.equal(missingCostume.name, 'Blue Guy 2');
|
||||
// Costume should have both default cosutme (e.g. Gray Question Mark) data and original data
|
||||
@@ -85,7 +85,7 @@ test('load and then save sprite2 with missing vector costume file', t => {
|
||||
|
||||
t.equal(resavedSprite.name, 'Blue Guy');
|
||||
t.equal(resavedSprite.costumes.length, 1);
|
||||
|
||||
|
||||
const missingCostume = resavedSprite.costumes[0];
|
||||
t.equal(missingCostume.name, 'Blue Guy 2');
|
||||
// Costume should have both default cosutme (e.g. Gray Question Mark) data and original data
|
||||
@@ -99,9 +99,8 @@ test('load and then save sprite2 with missing vector costume file', t => {
|
||||
|
||||
test('serializeCostume does not save data for missing costume', t => {
|
||||
const costumeDescs = serializeCostumes(vm.runtime, vm.runtime.targets[2].id);
|
||||
|
||||
|
||||
t.equal(costumeDescs.length, 0);
|
||||
|
||||
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
@@ -76,14 +76,14 @@ const test = tap.test;
|
||||
|
||||
test('load sprite3 with corrupted bitmap costume file', t => {
|
||||
t.equal(vm.runtime.targets.length, 3);
|
||||
|
||||
|
||||
const stage = vm.runtime.targets[0];
|
||||
t.ok(stage.isStage);
|
||||
|
||||
const greenGuySprite = vm.runtime.targets[2];
|
||||
t.equal(greenGuySprite.getName(), 'Green Guy');
|
||||
t.equal(greenGuySprite.getCostumes().length, 1);
|
||||
|
||||
|
||||
const corruptedCostume = greenGuySprite.getCostumes()[0];
|
||||
t.equal(corruptedCostume.name, 'Green Guy');
|
||||
t.equal(corruptedCostume.assetId, defaultBitmapAssetId);
|
||||
@@ -102,7 +102,7 @@ test('load and then save sprite with corrupted costume file', t => {
|
||||
|
||||
t.equal(resavedSprite.name, 'Green Guy');
|
||||
t.equal(resavedSprite.costumes.length, 1);
|
||||
|
||||
|
||||
const corruptedCostume = resavedSprite.costumes[0];
|
||||
t.equal(corruptedCostume.name, 'Green Guy');
|
||||
// Resaved project costume should have the metadata that corresponds to the original broken costume
|
||||
@@ -121,5 +121,4 @@ test('serializeCostume saves orignal broken costume', t => {
|
||||
t.equal(costume.fileName, `${brokenCostumeMd5}.png`);
|
||||
t.equal(md5(costume.fileContent), brokenCostumeMd5);
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
@@ -57,14 +57,14 @@ const test = tap.test;
|
||||
|
||||
test('load sprite3 with corrupted vector costume file', t => {
|
||||
t.equal(vm.runtime.targets.length, 3);
|
||||
|
||||
|
||||
const stage = vm.runtime.targets[0];
|
||||
t.ok(stage.isStage);
|
||||
|
||||
const blueGuySprite = vm.runtime.targets[2];
|
||||
t.equal(blueGuySprite.getName(), 'Blue Square Guy');
|
||||
t.equal(blueGuySprite.getCostumes().length, 1);
|
||||
|
||||
|
||||
const corruptedCostume = blueGuySprite.getCostumes()[0];
|
||||
t.equal(corruptedCostume.name, 'costume1');
|
||||
t.equal(corruptedCostume.assetId, defaultVectorAssetId);
|
||||
@@ -83,7 +83,7 @@ test('load and then save sprite with corrupted costume file', t => {
|
||||
|
||||
t.equal(resavedSprite.name, 'Blue Square Guy');
|
||||
t.equal(resavedSprite.costumes.length, 1);
|
||||
|
||||
|
||||
const corruptedCostume = resavedSprite.costumes[0];
|
||||
t.equal(corruptedCostume.name, 'costume1');
|
||||
// Resaved project costume should have the metadata that corresponds to the original broken costume
|
||||
@@ -102,5 +102,4 @@ test('serializeCostume saves orignal broken costume', t => {
|
||||
t.equal(costume.fileName, `${brokenCostumeMd5}.svg`);
|
||||
t.equal(md5(costume.fileContent), brokenCostumeMd5);
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
@@ -59,14 +59,14 @@ const test = tap.test;
|
||||
|
||||
test('loading sprite3 with missing bitmap costume file', t => {
|
||||
t.equal(vm.runtime.targets.length, 3);
|
||||
|
||||
|
||||
const stage = vm.runtime.targets[0];
|
||||
t.ok(stage.isStage);
|
||||
|
||||
const greenGuySprite = vm.runtime.targets[2];
|
||||
t.equal(greenGuySprite.getName(), 'Green Guy');
|
||||
t.equal(greenGuySprite.getCostumes().length, 1);
|
||||
|
||||
|
||||
const missingCostume = greenGuySprite.getCostumes()[0];
|
||||
t.equal(missingCostume.name, 'Green Guy');
|
||||
// Costume should have both default cosutme (e.g. Gray Question Mark) data and original data
|
||||
@@ -85,7 +85,7 @@ test('load and then save sprite3 with missing bitmap costume file', t => {
|
||||
|
||||
t.equal(resavedSprite.name, 'Green Guy');
|
||||
t.equal(resavedSprite.costumes.length, 1);
|
||||
|
||||
|
||||
const missingCostume = resavedSprite.costumes[0];
|
||||
t.equal(missingCostume.name, 'Green Guy');
|
||||
// Costume should have both default cosutme (e.g. Gray Question Mark) data and original data
|
||||
@@ -99,9 +99,8 @@ test('load and then save sprite3 with missing bitmap costume file', t => {
|
||||
|
||||
test('serializeCostume does not save data for missing costume', t => {
|
||||
const costumeDescs = serializeCostumes(vm.runtime, vm.runtime.targets[2].id);
|
||||
|
||||
|
||||
t.equal(costumeDescs.length, 0);
|
||||
|
||||
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
@@ -39,14 +39,14 @@ const test = tap.test;
|
||||
|
||||
test('loading sprite3 with missing vector costume file', t => {
|
||||
t.equal(vm.runtime.targets.length, 3);
|
||||
|
||||
|
||||
const stage = vm.runtime.targets[0];
|
||||
t.ok(stage.isStage);
|
||||
|
||||
const blueGuySprite = vm.runtime.targets[2];
|
||||
t.equal(blueGuySprite.getName(), 'Blue Square Guy');
|
||||
t.equal(blueGuySprite.getCostumes().length, 1);
|
||||
|
||||
|
||||
const missingCostume = blueGuySprite.getCostumes()[0];
|
||||
t.equal(missingCostume.name, 'costume1');
|
||||
// Costume should have both default cosutme (e.g. Gray Question Mark) data and original data
|
||||
@@ -65,7 +65,7 @@ test('load and then save sprite3 with missing vector costume file', t => {
|
||||
|
||||
t.equal(resavedSprite.name, 'Blue Square Guy');
|
||||
t.equal(resavedSprite.costumes.length, 1);
|
||||
|
||||
|
||||
const missingCostume = resavedSprite.costumes[0];
|
||||
t.equal(missingCostume.name, 'costume1');
|
||||
// Costume should have both default cosutme (e.g. Gray Question Mark) data and original data
|
||||
@@ -79,9 +79,8 @@ test('load and then save sprite3 with missing vector costume file', t => {
|
||||
|
||||
test('serializeCostume does not save data for missing costume', t => {
|
||||
const costumeDescs = serializeCostumes(vm.runtime, vm.runtime.targets[2].id);
|
||||
|
||||
|
||||
t.equal(costumeDescs.length, 0);
|
||||
|
||||
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
@@ -22,8 +22,8 @@ test('stack click activates the stack', t => {
|
||||
vm.on('playgroundData', () => {
|
||||
// The sprite should have moved 100 to the right
|
||||
t.equal(vm.editingTarget.x, 100);
|
||||
vm.quit();
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
|
||||
// Start VM, load project, and run
|
||||
|
||||
@@ -48,7 +48,7 @@ test('unknown opcode', t => {
|
||||
t.true(blocks.getBlock(fourthBlockInputId).shadow);
|
||||
t.equal(blocks.getBlock(fourthBlockInputId).opcode, 'sound_sounds_menu');
|
||||
|
||||
vm.quit();
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
});
|
||||
@@ -33,7 +33,7 @@ test('unknown opcode', t => {
|
||||
t.equal(blocks.getBlock(secondBlockId).opcode, 'control_forever');
|
||||
t.equal(innerBlockId, null);
|
||||
|
||||
vm.quit();
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
});
|
||||
@@ -49,7 +49,7 @@ test('unknown opcode', t => {
|
||||
t.equal(undefinedComment.x, 0);
|
||||
t.equal(undefinedComment.y, 0);
|
||||
|
||||
vm.quit();
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
});
|
||||
@@ -35,8 +35,8 @@ test('importing one project after the other resets monitored variables', t => {
|
||||
const jamalVarBlock = vm.runtime.monitorBlocks.getBlock(jamalVarId);
|
||||
t.notOk(jamalVarBlock);
|
||||
|
||||
vm.quit();
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -114,8 +114,8 @@ test('importing sb2 project with special chars in variable names', t => {
|
||||
t.equal(bananasVarBlocks.length, 1);
|
||||
t.equal(bananasVarBlocks[0].fields.VARIABLE.id, ltPerfectVarId);
|
||||
|
||||
vm.quit();
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
|
||||
// Start VM, load project, and run
|
||||
|
||||
@@ -114,8 +114,8 @@ test('importing sb3 project with special chars in variable names', t => {
|
||||
t.equal(bananasVarBlocks.length, 1);
|
||||
t.equal(bananasVarBlocks[0].fields.VARIABLE.id, ltPerfectVarId);
|
||||
|
||||
vm.quit();
|
||||
t.end();
|
||||
process.nextTick(process.exit);
|
||||
});
|
||||
|
||||
// Start VM, load project, and run
|
||||
|
||||
@@ -261,7 +261,8 @@ test('wait', t => {
|
||||
const args = {DURATION: .01};
|
||||
const waitTime = args.DURATION * 1000;
|
||||
const startTest = Date.now();
|
||||
const threshold = 1000 / 60; // 60 hz
|
||||
const thresholdSmall = 1000 / 60; // only allow the wait to end one 60Hz frame early
|
||||
const thresholdLarge = 1000 / 3; // be less picky about when the wait ends, in case CPU load makes the VM run slowly
|
||||
let yields = 0;
|
||||
const util = new BlockUtility();
|
||||
const mockUtil = {
|
||||
@@ -280,7 +281,7 @@ test('wait', t => {
|
||||
while (timeElapsed < waitTime) {
|
||||
timeElapsed = mockUtil.stackFrame.timer.timeElapsed();
|
||||
// In case util.timer is broken - have our own "exit"
|
||||
if (Date.now() - startTest > timeElapsed + threshold) {
|
||||
if (Date.now() - startTest > timeElapsed + thresholdSmall) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -288,7 +289,9 @@ test('wait', t => {
|
||||
c.wait(args, mockUtil);
|
||||
t.equal(yields, 1, 'Second call after timeElapsed does not yield');
|
||||
t.equal(waitTime, mockUtil.stackFrame.duration);
|
||||
t.ok(timeElapsed >= (waitTime - threshold) &&
|
||||
timeElapsed <= (waitTime + threshold));
|
||||
t.ok(timeElapsed >= (waitTime - thresholdSmall),
|
||||
'Wait block ended too early: ${timeElapsed} < ${waitTime} - ${thresholdSmall}');
|
||||
t.ok(timeElapsed <= (waitTime + thresholdLarge),
|
||||
'Wait block ended too late: ${timeElapsed} > ${waitTime} + ${thresholdLarge}');
|
||||
t.end();
|
||||
});
|
||||
@@ -6,8 +6,6 @@ const Runtime = require('../../src/engine/runtime');
|
||||
const MonitorRecord = require('../../src/engine/monitor-record');
|
||||
const {Map} = require('immutable');
|
||||
|
||||
tap.tearDown(() => process.nextTick(process.exit));
|
||||
|
||||
const test = tap.test;
|
||||
|
||||
test('spec', t => {
|
||||
@@ -193,6 +191,7 @@ test('Starting the runtime emits an event', t => {
|
||||
});
|
||||
rt.start();
|
||||
t.equal(started, true);
|
||||
rt.quit();
|
||||
t.end();
|
||||
});
|
||||
|
||||
@@ -209,6 +208,7 @@ test('Runtime cannot be started while already running', t => {
|
||||
// Starting again should not emit another event
|
||||
rt.start();
|
||||
t.equal(started, false);
|
||||
rt.quit();
|
||||
t.end();
|
||||
});
|
||||
|
||||
@@ -224,6 +224,7 @@ test('setCompatibilityMode restarts if it was already running', t => {
|
||||
|
||||
rt.setCompatibilityMode(true);
|
||||
t.equal(started, true);
|
||||
rt.quit();
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
@@ -27,8 +27,6 @@ tap.beforeEach(() => {
|
||||
});
|
||||
});
|
||||
|
||||
tap.tearDown(() => process.nextTick(process.exit));
|
||||
|
||||
const test = tap.test;
|
||||
|
||||
test('Adding a sprite (from sprite2) should emit a project changed event', t => {
|
||||
|
||||
@@ -53,8 +53,6 @@ tap.beforeEach(() => {
|
||||
});
|
||||
});
|
||||
|
||||
tap.tearDown(() => process.nextTick(process.exit));
|
||||
|
||||
const test = tap.test;
|
||||
|
||||
test('Creating a block should emit a project changed event', t => {
|
||||
|
||||
@@ -4,8 +4,6 @@ const readFileToBuffer = require('../fixtures/readProjectFile').readFileToBuffer
|
||||
const makeTestStorage = require('../fixtures/make-test-storage');
|
||||
const VirtualMachine = require('../../src/virtual-machine');
|
||||
|
||||
tap.tearDown(() => process.nextTick(process.exit));
|
||||
|
||||
const test = tap.test;
|
||||
|
||||
// Test that loading a project does not emit a project change
|
||||
|
||||
@@ -6,12 +6,11 @@ const test = tap.test;
|
||||
let originals;
|
||||
let newBlocks;
|
||||
|
||||
tap.beforeEach(done => {
|
||||
tap.beforeEach(() => {
|
||||
originals = simpleStack;
|
||||
// Will be mutated so make a copy first
|
||||
newBlocks = JSON.parse(JSON.stringify(simpleStack));
|
||||
newBlockIds(newBlocks);
|
||||
done();
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -8,8 +8,6 @@ const Renderer = require('../fixtures/fake-renderer');
|
||||
const Runtime = require('../../src/engine/runtime');
|
||||
const RenderedTarget = require('../../src/sprites/rendered-target');
|
||||
|
||||
tap.tearDown(() => process.nextTick(process.exit));
|
||||
|
||||
const test = tap.test;
|
||||
|
||||
test('deleteSound returns function after deleting or null if nothing was deleted', t => {
|
||||
@@ -1016,6 +1014,7 @@ test('Starting the VM emits an event', t => {
|
||||
});
|
||||
vm.start();
|
||||
t.equal(started, true);
|
||||
vm.quit();
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user