Spaces:
Running
Running
File size: 9,151 Bytes
30c32c8 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 |
const test = require('tap').test;
const Sensing = require('../../src/blocks/scratch3_sensing');
const Runtime = require('../../src/engine/runtime');
const Sprite = require('../../src/sprites/sprite');
const RenderedTarget = require('../../src/sprites/rendered-target');
const BlockUtility = require('../../src/engine/block-utility');
test('getPrimitives', t => {
const rt = new Runtime();
const s = new Sensing(rt);
t.type(s.getPrimitives(), 'object');
t.end();
});
test('ask and answer with a hidden target', t => {
const rt = new Runtime();
const s = new Sensing(rt);
const util = {target: {visible: false}};
const expectedQuestion = 'a question';
const expectedAnswer = 'the answer';
// Test is written out of order because of promises, follow the (#) comments.
rt.addListener('QUESTION', question => {
// (2) Assert the question is correct, then emit the answer
t.strictEqual(question, expectedQuestion);
rt.emit('ANSWER', expectedAnswer);
});
// (1) Emit the question.
const promise = s.askAndWait({QUESTION: expectedQuestion}, util);
// (3) Ask block resolves after the answer is emitted.
promise.then(() => {
t.strictEqual(s.getAnswer(), expectedAnswer);
t.end();
});
});
test('ask and stop all dismisses question', t => {
const rt = new Runtime();
const s = new Sensing(rt);
const util = {target: {visible: false}};
const expectedQuestion = 'a question';
let call = 0;
rt.addListener('QUESTION', question => {
if (call === 0) {
// (2) Assert the question was passed.
t.strictEqual(question, expectedQuestion);
} else if (call === 1) {
// (4) Assert the question was dismissed.
t.strictEqual(question, null);
t.end();
}
call += 1;
});
// (1) Emit the question.
s.askAndWait({QUESTION: expectedQuestion}, util);
// (3) Emit the stop all event.
rt.stopAll();
});
test('ask and stop other scripts dismisses if it is the last question', t => {
const rt = new Runtime();
const s = new Sensing(rt);
const util = {target: {visible: false, sprite: {}, getCustomState: () => ({})}, thread: {}};
const expectedQuestion = 'a question';
let call = 0;
rt.addListener('QUESTION', question => {
if (call === 0) {
// (2) Assert the question was passed.
t.strictEqual(question, expectedQuestion);
} else if (call === 1) {
// (4) Assert the question was dismissed.
t.strictEqual(question, null);
t.end();
}
call += 1;
});
// (1) Emit the questions.
s.askAndWait({QUESTION: expectedQuestion}, util);
// (3) Emit the stop for target event.
rt.stopForTarget(util.target, util.thread);
});
test('ask and stop other scripts asks next question', t => {
const rt = new Runtime();
const s = new Sensing(rt);
const util = {target: {visible: false, sprite: {}, getCustomState: () => ({})}, thread: {}};
const util2 = {target: {visible: false, sprite: {}, getCustomState: () => ({})}, thread: {}};
const expectedQuestion = 'a question';
const nextQuestion = 'a followup';
let call = 0;
rt.addListener('QUESTION', question => {
if (call === 0) {
// (2) Assert the question was passed.
t.strictEqual(question, expectedQuestion);
} else if (call === 1) {
// (4) Assert the next question was passed.
t.strictEqual(question, nextQuestion);
t.end();
}
call += 1;
});
// (1) Emit the questions.
s.askAndWait({QUESTION: expectedQuestion}, util);
s.askAndWait({QUESTION: nextQuestion}, util2);
// (3) Emit the stop for target event.
rt.stopForTarget(util.target, util.thread);
});
test('ask and answer with a visible target', t => {
const rt = new Runtime();
const s = new Sensing(rt);
const util = {target: {visible: true}};
const expectedQuestion = 'a question';
const expectedAnswer = 'the answer';
rt.removeAllListeners('SAY'); // Prevent say blocks from executing
rt.addListener('SAY', (target, type, question) => {
// Should emit SAY with the question
t.strictEqual(question, expectedQuestion);
});
rt.addListener('QUESTION', question => {
// Question should be blank for a visible target
t.strictEqual(question, '');
// Remove the say listener and add a new one to assert bubble is cleared
// by setting say to empty string after answer is received.
rt.removeAllListeners('SAY');
rt.addListener('SAY', (target, type, text) => {
t.strictEqual(text, '');
t.end();
});
rt.emit('ANSWER', expectedAnswer);
});
s.askAndWait({QUESTION: expectedQuestion}, util);
});
test('answer gets reset when runtime is disposed', t => {
const rt = new Runtime();
const s = new Sensing(rt);
const util = {target: {visible: false}};
const expectedAnswer = 'the answer';
rt.addListener('QUESTION', () => rt.emit('ANSWER', expectedAnswer));
const promise = s.askAndWait({QUESTION: ''}, util);
promise.then(() => t.strictEqual(s.getAnswer(), expectedAnswer))
.then(() => rt.dispose())
.then(() => {
t.strictEqual(s.getAnswer(), '');
t.end();
});
});
test('set drag mode', t => {
const runtime = new Runtime();
runtime.requestTargetsUpdate = () => {}; // noop for testing
const sensing = new Sensing(runtime);
const s = new Sprite(null, runtime);
const rt = new RenderedTarget(s, runtime);
sensing.setDragMode({DRAG_MODE: 'not draggable'}, {target: rt});
t.strictEqual(rt.draggable, false);
sensing.setDragMode({DRAG_MODE: 'draggable'}, {target: rt});
t.strictEqual(rt.draggable, true);
t.end();
});
test('get loudness with caching', t => {
const rt = new Runtime();
const sensing = new Sensing(rt);
// It should report -1 when audio engine is not available.
t.strictEqual(sensing.getLoudness(), -1);
// Stub the audio engine with its getLoudness function, and set up different
// values to simulate it changing over time.
const firstLoudness = 1;
const secondLoudness = 2;
let simulatedLoudness = firstLoudness;
rt.audioEngine = {getLoudness: () => simulatedLoudness};
// It should report -1 when current step time is null.
// TW: The concept of a null current step time is inherently flawed and removed in TurboWarp.
// t.strictEqual(sensing.getLoudness(), -1);
// Stub the current step time.
rt.currentStepTime = 1000 / 30;
// The first time it works, it should report the result from the stubbed audio engine.
t.strictEqual(sensing.getLoudness(), firstLoudness);
// Update the simulated loudness to a new value.
simulatedLoudness = secondLoudness;
// Simulate time passing by advancing the timer forward a little bit.
// After less than a step, it should still report cached loudness.
let simulatedTime = Date.now() + (rt.currentStepTime / 2);
sensing._timer = {time: () => simulatedTime};
t.strictEqual(sensing.getLoudness(), firstLoudness);
// Simulate more than a step passing. It should now request the value
// from the audio engine again.
simulatedTime += rt.currentStepTime;
t.strictEqual(sensing.getLoudness(), secondLoudness);
t.end();
});
test('loud? boolean', t => {
const rt = new Runtime();
const sensing = new Sensing(rt);
// The simplest way to test this is to actually override the getLoudness
// method, which isLoud uses.
let simulatedLoudness = 0;
sensing.getLoudness = () => simulatedLoudness;
t.false(sensing.isLoud());
// Check for GREATER than 10, not equal.
simulatedLoudness = 10;
t.false(sensing.isLoud());
simulatedLoudness = 11;
t.true(sensing.isLoud());
t.end();
});
test('get attribute of sprite variable', t => {
const rt = new Runtime();
const sensing = new Sensing(rt);
const s = new Sprite(null, rt);
const target = new RenderedTarget(s, rt);
const variable = {
name: 'cars',
value: 'trucks',
type: ''
};
// Add variable to set the map (it should be empty before this).
target.variables.anId = variable;
rt.getSpriteTargetByName = () => target;
t.equal(sensing.getAttributeOf({PROPERTY: 'cars'}), 'trucks');
t.end();
});
test('get attribute of variable that does not exist', t => {
const rt = new Runtime();
const sensing = new Sensing(rt);
const s = new Sprite(null, rt);
const target = new RenderedTarget(s, rt);
rt.getTargetForStage = () => target;
t.equal(sensing.getAttributeOf({PROPERTY: 'variableThatDoesNotExist'}), 0);
t.end();
});
test('username block', t => {
const rt = new Runtime();
const sensing = new Sensing(rt);
const util = new BlockUtility(rt.sequencer);
t.equal(sensing.getUsername({}, util), '');
t.end();
});
|