Some checks failed
CI / go (push) Waiting to run
CI / docker (push) Waiting to run
CI / lint-go (push) Waiting to run
CI / lint-docs (push) Waiting to run
CI / check-paperclip (push) Waiting to run
Deploy static site / Deploy to GitHub Pages (push) Has been cancelled
Deploy static site / Deploy via Docker to hatch.surf (push) Has been cancelled
Resolved conflicts by taking GitHub versions for: - .dockerignore, .gitignore, Dockerfile, README.md, docker-compose.yml Kept deploy.sh updated to: - Pull from GitHub (primary source) - Push to Gitea (push-mirror) - Build from site/ directory (GitHub structure) Co-Authored-By: Paperclip <noreply@paperclip.ing>
253 lines
7.7 KiB
JavaScript
253 lines
7.7 KiB
JavaScript
'use strict';
|
|
|
|
var test = require('tape');
|
|
var forEach = require('for-each');
|
|
var debug = require('object-inspect');
|
|
var v = require('es-value-fixtures');
|
|
|
|
var CompletionRecord = require('es-abstract/2025/CompletionRecord');
|
|
var NormalCompletion = require('es-abstract/2025/NormalCompletion');
|
|
var ReturnCompletion = require('es-abstract/2025/ReturnCompletion');
|
|
var ThrowCompletion = require('es-abstract/2025/ThrowCompletion');
|
|
|
|
var SLOT = require('internal-slot');
|
|
var IteratorCloseAll = require('../aos/IteratorCloseAll');
|
|
var IfAbruptCloseIterators = require('../aos/IfAbruptCloseIterators');
|
|
var GetOptionsObject = require('../aos/GetOptionsObject');
|
|
var GeneratorResumeAbrupt = require('../aos/GeneratorResumeAbrupt');
|
|
|
|
var makeGenerator = function (state) {
|
|
var gen = {};
|
|
SLOT.set(gen, '[[GeneratorState]]', state);
|
|
SLOT.set(gen, '[[GeneratorBrand]]', 'test');
|
|
SLOT.set(gen, '[[GeneratorContext]]', null);
|
|
SLOT.set(gen, '[[CloseIfAbrupt]]', null);
|
|
return gen;
|
|
};
|
|
|
|
test('GeneratorResumeAbrupt', function (t) {
|
|
t.test('ThrowCompletion on SUSPENDED-START state', function (st) {
|
|
var gen = makeGenerator('SUSPENDED-START');
|
|
|
|
st['throws'](
|
|
function () { GeneratorResumeAbrupt(gen, ThrowCompletion(new EvalError('test')), 'test'); },
|
|
EvalError,
|
|
'throw completion re-throws on SUSPENDED-START (transitions to COMPLETED first)'
|
|
);
|
|
st.equal(SLOT.get(gen, '[[GeneratorState]]'), 'COMPLETED', 'state is COMPLETED after throw');
|
|
st.end();
|
|
});
|
|
|
|
t.test('ThrowCompletion on COMPLETED state', function (st) {
|
|
var gen = makeGenerator('COMPLETED');
|
|
|
|
st['throws'](
|
|
function () { GeneratorResumeAbrupt(gen, ThrowCompletion(new EvalError('test')), 'test'); },
|
|
EvalError,
|
|
'throw completion re-throws on COMPLETED'
|
|
);
|
|
st.end();
|
|
});
|
|
|
|
t.test('ReturnCompletion on COMPLETED state', function (st) {
|
|
var gen = makeGenerator('COMPLETED');
|
|
|
|
var result = GeneratorResumeAbrupt(gen, ReturnCompletion(42), 'test');
|
|
st.deepEqual(result, { value: 42, done: true }, 'return completion returns {value, done: true}');
|
|
st.end();
|
|
});
|
|
|
|
t.test('ThrowCompletion on SUSPENDED-YIELD calls genContext', function (st) {
|
|
var gen = makeGenerator('SUSPENDED-YIELD');
|
|
var contextCalledWith = null;
|
|
SLOT.set(gen, '[[GeneratorContext]]', function (val) {
|
|
contextCalledWith = val;
|
|
return { value: undefined, done: true };
|
|
});
|
|
|
|
var result = GeneratorResumeAbrupt(gen, ThrowCompletion(new EvalError('injected')), 'test');
|
|
st.ok(contextCalledWith instanceof EvalError, 'genContext called with the error value');
|
|
st.deepEqual(result, { value: undefined, done: true }, 'returns genContext result');
|
|
st.end();
|
|
});
|
|
|
|
t.test('ReturnCompletion on SUSPENDED-YIELD calls closeIfAbrupt', function (st) {
|
|
var gen = makeGenerator('SUSPENDED-YIELD');
|
|
var closeCalledWith = null;
|
|
SLOT.set(gen, '[[GeneratorContext]]', function () { return { value: undefined, done: true }; });
|
|
SLOT.set(gen, '[[CloseIfAbrupt]]', function (completion) {
|
|
closeCalledWith = completion;
|
|
});
|
|
|
|
var result = GeneratorResumeAbrupt(gen, ReturnCompletion(99), 'test');
|
|
st.ok(closeCalledWith instanceof CompletionRecord, 'closeIfAbrupt called with CompletionRecord');
|
|
st.equal(closeCalledWith.type(), 'return', 'completion is a return');
|
|
st.equal(closeCalledWith.value(), 99, 'completion value is 99');
|
|
st.deepEqual(result, { value: 99, done: true }, 'returns {value: 99, done: true}');
|
|
st.equal(SLOT.get(gen, '[[GeneratorState]]'), 'COMPLETED', 'state is COMPLETED');
|
|
st.end();
|
|
});
|
|
|
|
t.end();
|
|
});
|
|
|
|
test('GetOptionsObject', function (t) {
|
|
t.test('undefined returns null-prototype object', function (st) {
|
|
var result = GetOptionsObject(void undefined);
|
|
st.equal(typeof result, 'object', 'returns an object');
|
|
st.equal(Object.getPrototypeOf(result), null, 'prototype is null');
|
|
st.end();
|
|
});
|
|
|
|
t.test('object returns itself', function (st) {
|
|
var obj = { a: 1 };
|
|
st.equal(GetOptionsObject(obj), obj, 'returns the same object');
|
|
st.end();
|
|
});
|
|
|
|
t.test('non-object, non-undefined throws TypeError', function (st) {
|
|
forEach(v.primitives, function (primitive) {
|
|
if (typeof primitive !== 'undefined') {
|
|
st['throws'](
|
|
function () { GetOptionsObject(primitive); },
|
|
TypeError,
|
|
debug(primitive) + ' throws TypeError'
|
|
);
|
|
}
|
|
});
|
|
st.end();
|
|
});
|
|
|
|
t.end();
|
|
});
|
|
|
|
test('IfAbruptCloseIterators', function (t) {
|
|
t.test('normal completion returns value', function (st) {
|
|
var result = IfAbruptCloseIterators(NormalCompletion(42), []);
|
|
st.equal(result, 42, 'returns unwrapped value');
|
|
st.end();
|
|
});
|
|
|
|
t.test('throw completion closes iterators and throws', function (st) {
|
|
var closeCalled = false;
|
|
var iterRecord = {
|
|
'[[Iterator]]': {
|
|
next: function () { return { done: true }; },
|
|
'return': function () { closeCalled = true; return { done: true }; }
|
|
},
|
|
'[[NextMethod]]': function () { return { done: true }; },
|
|
'[[Done]]': false
|
|
};
|
|
|
|
st['throws'](
|
|
function () { IfAbruptCloseIterators(ThrowCompletion(new EvalError('test')), [iterRecord]); },
|
|
EvalError,
|
|
'throws the error'
|
|
);
|
|
st.equal(closeCalled, true, 'iterator was closed');
|
|
st.end();
|
|
});
|
|
|
|
t.test('non-CompletionRecord throws TypeError', function (st) {
|
|
st['throws'](
|
|
function () { IfAbruptCloseIterators(42, []); },
|
|
TypeError,
|
|
'non-CompletionRecord throws'
|
|
);
|
|
st.end();
|
|
});
|
|
|
|
t.end();
|
|
});
|
|
|
|
test('IteratorCloseAll', function (t) {
|
|
t.test('closes all iterators in reverse order', function (st) {
|
|
var closed = [];
|
|
var makeIter = function (name) {
|
|
return {
|
|
'[[Iterator]]': {
|
|
next: function () { return { done: true }; },
|
|
'return': function () { closed.push(name); return { done: true }; }
|
|
},
|
|
'[[NextMethod]]': function () { return { done: true }; },
|
|
'[[Done]]': false
|
|
};
|
|
};
|
|
|
|
st['throws'](
|
|
function () { IteratorCloseAll([makeIter('a'), makeIter('b'), makeIter('c')], ThrowCompletion(new EvalError('test'))); },
|
|
EvalError,
|
|
'throws the completion'
|
|
);
|
|
st.deepEqual(closed, ['c', 'b', 'a'], 'closed in reverse order');
|
|
st.end();
|
|
});
|
|
|
|
t.test('close error replaces normal completion', function (st) {
|
|
var iter = {
|
|
'[[Iterator]]': {
|
|
next: function () { return { done: true }; },
|
|
'return': function () { throw new SyntaxError('close error'); }
|
|
},
|
|
'[[NextMethod]]': function () { return { done: true }; },
|
|
'[[Done]]': false
|
|
};
|
|
|
|
st['throws'](
|
|
function () { IteratorCloseAll([iter], NormalCompletion(void undefined)); },
|
|
SyntaxError,
|
|
'close error replaces normal completion'
|
|
);
|
|
st.end();
|
|
});
|
|
|
|
t.test('original throw completion wins over close error', function (st) {
|
|
var iter = {
|
|
'[[Iterator]]': {
|
|
next: function () { return { done: true }; },
|
|
'return': function () { throw new SyntaxError('close error'); }
|
|
},
|
|
'[[NextMethod]]': function () { return { done: true }; },
|
|
'[[Done]]': false
|
|
};
|
|
|
|
st['throws'](
|
|
function () { IteratorCloseAll([iter], ThrowCompletion(new EvalError('original'))); },
|
|
EvalError,
|
|
'original throw completion takes precedence'
|
|
);
|
|
st.end();
|
|
});
|
|
|
|
t.test('normal completion after successful close returns undefined', function (st) {
|
|
var iter = {
|
|
'[[Iterator]]': {
|
|
next: function () { return { done: true }; },
|
|
'return': function () { return { done: true }; }
|
|
},
|
|
'[[NextMethod]]': function () { return { done: true }; },
|
|
'[[Done]]': false
|
|
};
|
|
|
|
var result = IteratorCloseAll([iter], NormalCompletion(void undefined));
|
|
st.equal(result, void undefined, 'returns undefined');
|
|
st.end();
|
|
});
|
|
|
|
t.test('validation', function (st) {
|
|
st['throws'](
|
|
function () { IteratorCloseAll('not an array', NormalCompletion(void undefined)); },
|
|
TypeError,
|
|
'non-array iters throws'
|
|
);
|
|
st['throws'](
|
|
function () { IteratorCloseAll([], 'not a completion'); },
|
|
TypeError,
|
|
'non-CompletionRecord throws'
|
|
);
|
|
st.end();
|
|
});
|
|
|
|
t.end();
|
|
});
|