diff --git a/js/modulestest/compile.go b/js/modulestest/compile.go index 5b4c882926d..b3931e065af 100644 --- a/js/modulestest/compile.go +++ b/js/modulestest/compile.go @@ -2,7 +2,6 @@ package modulestest import ( "io" - "io/fs" "os" "path" "path/filepath" @@ -34,16 +33,6 @@ func CompileFile(base, name string) (*goja.Program, error) { return compile(name, b) } -// CompileFileFromFS compiles a JS file like CompileFile, but using a [fs.FS] as base. -func CompileFileFromFS(base fs.FS, name string) (*goja.Program, error) { - b, err := fs.ReadFile(base, name) - if err != nil { - return nil, err - } - - return compile(name, b) -} - func compile(name string, b []byte) (*goja.Program, error) { program, err := goja.Compile(name, string(b), false) if err != nil { diff --git a/js/modulestest/console.go b/js/modulestest/console.go deleted file mode 100644 index de465915409..00000000000 --- a/js/modulestest/console.go +++ /dev/null @@ -1,64 +0,0 @@ -package modulestest - -// IMPORTANT: This file is a subset of the `js/console.go` file, as the original file doesn't expose -// the `console` struct implementation, which is needed for the `console.log` function to work in tests. - -import ( - "encoding/json" - "strings" - - "github.com/dop251/goja" - "github.com/sirupsen/logrus" -) - -type console struct { - logger logrus.FieldLogger -} - -// Creates a console with the standard logrus logger. -func newConsole(logger logrus.FieldLogger) *console { - return &console{logger.WithField("source", "console")} -} - -func (c console) Log(args ...goja.Value) { - c.Info(args...) -} - -func (c console) Info(args ...goja.Value) { - c.log(logrus.InfoLevel, args...) -} - -func (c console) log(level logrus.Level, args ...goja.Value) { - var strs strings.Builder - for i := 0; i < len(args); i++ { - if i > 0 { - strs.WriteString(" ") - } - strs.WriteString(c.valueString(args[i])) - } - msg := strs.String() - - switch level { //nolint:exhaustive - case logrus.DebugLevel: - c.logger.Debug(msg) - case logrus.InfoLevel: - c.logger.Info(msg) - case logrus.WarnLevel: - c.logger.Warn(msg) - case logrus.ErrorLevel: - c.logger.Error(msg) - } -} - -func (c console) valueString(v goja.Value) string { - mv, ok := v.(json.Marshaler) - if !ok { - return v.String() - } - - b, err := json.Marshal(mv) - if err != nil { - return v.String() - } - return string(b) -} diff --git a/js/modulestest/runtime.go b/js/modulestest/runtime.go index 056129aade8..26cd54244f3 100644 --- a/js/modulestest/runtime.go +++ b/js/modulestest/runtime.go @@ -3,14 +3,10 @@ package modulestest import ( "context" - "embed" - "fmt" - "io/fs" "net/url" "testing" "github.com/dop251/goja" - "github.com/stretchr/testify/require" "go.k6.io/k6/js/common" "go.k6.io/k6/js/compiler" "go.k6.io/k6/js/eventloop" @@ -111,40 +107,3 @@ func (r *Runtime) innerSetupModuleSystem() error { impl := modules.NewLegacyRequireImpl(r.VU, ms, url.URL{}) return r.VU.RuntimeField.Set("require", impl.Require) } - -//go:embed wptutils/* -var wptutils embed.FS - -// NewRuntimeForWPT will create a new test runtime like NewRuntime, but ready to be used -// for Web Platform Tests (https://github.com/web-platform-tests/wpt). -func NewRuntimeForWPT(t testing.TB) *Runtime { - var err error - runtime := NewRuntime(t) - - // We want to make the [console.log()] available for Web Platform Tests, as it - // is very useful for debugging, because we don't have a real debugger for JS code. - logger := runtime.VU.InitEnvField.Logger - require.NoError(t, runtime.VU.RuntimeField.Set("console", newConsole(logger))) - - // We compile the Web Platform Tests harness scripts into a goja.Program, - // and execute them in the goja runtime in order to make the Web Platform - // assertion functions available to the tests. - files, err := fs.ReadDir(wptutils, "wptutils") - require.NoError(t, err) - - for _, file := range files { - // Skip directories for safety, - // as we expect all files to be present in the root. - if file.IsDir() { - continue - } - - program, err := CompileFileFromFS(wptutils, fmt.Sprintf("wptutils/%s", file.Name())) - require.NoError(t, err) - - _, err = runtime.VU.Runtime().RunProgram(program) - require.NoError(t, err) - } - - return runtime -} diff --git a/js/modulestest/wptutils/readable-stream-to-array.js b/js/modulestest/wptutils/readable-stream-to-array.js deleted file mode 100644 index 9e6dc85ae71..00000000000 --- a/js/modulestest/wptutils/readable-stream-to-array.js +++ /dev/null @@ -1,26 +0,0 @@ -// Original source file: https://github.com/web-platform-tests/wpt/blob/3ede6629030918b00941c2fb7d176a18cbea16ea/encoding/streams/resources/readable-stream-to-array.js -'use strict'; - -// NOTE: This is a simplified version of the original implementation -// found at: https://github.com/web-platform-tests/wpt/blob/3ede6629030918b00941c2fb7d176a18cbea16ea/encoding/streams/resources/readable-stream-to-array.js#L3 -// that doesn't rely on WritableStreams. -function readableStreamToArray(stream) { - const reader = stream.getReader(); - const array = []; - - // A function to recursively read chunks from the stream - function readNextChunk() { - return reader.read().then(({done, value}) => { - if (done) { - // Stream has been fully read - return array; - } - // Add the chunk to the array - array.push(value); - // Recursively read the next chunk - return readNextChunk(); - }); - } - - return readNextChunk(); -} \ No newline at end of file diff --git a/js/modulestest/wptutils/rs-test-templates.js b/js/modulestest/wptutils/rs-test-templates.js deleted file mode 100644 index d627e18b56e..00000000000 --- a/js/modulestest/wptutils/rs-test-templates.js +++ /dev/null @@ -1,728 +0,0 @@ -// Original source file: https://github.com/web-platform-tests/wpt/blob/99d74f9529e16ec0722ef11136ab29b9e80fff26/streams/resources/rs-test-templates.js -// -// This source file has been slightly modified for this project. -// But it contains the same functions and checks in essence. -// -'use strict'; - -// These tests can be run against any readable stream produced by the web platform that meets the given descriptions. -// For readable stream tests, the factory should return the stream. For reader tests, the factory should return a -// { stream, reader } object. (You can use this to vary the time at which you acquire a reader.) - -function templatedRSEmpty(label, factory) { - test(() => {}, 'Running templatedRSEmpty with ' + label); - - test(() => { - - const rs = factory(); - - assert_equals(typeof rs.locked, 'boolean', 'has a boolean locked getter'); - assert_equals(typeof rs.cancel, 'function', 'has a cancel method'); - assert_equals(typeof rs.getReader, 'function', 'has a getReader method'); - // FIXME: Uncomment once we add that support - // assert_equals(typeof rs.pipeThrough, 'function', 'has a pipeThrough method'); - // assert_equals(typeof rs.pipeTo, 'function', 'has a pipeTo method'); - // assert_equals(typeof rs.tee, 'function', 'has a tee method'); - - }, label + ': instances have the correct methods and properties'); - - test(() => { - const rs = factory(); - - assert_throws_js(TypeError, () => rs.getReader({ mode: '' }), 'empty string mode should throw'); - assert_throws_js(TypeError, () => rs.getReader({ mode: null }), 'null mode should throw'); - assert_throws_js(TypeError, () => rs.getReader({ mode: 'asdf' }), 'asdf mode should throw'); - assert_throws_js(TypeError, () => rs.getReader(5), '5 should throw'); - - // Should not throw - rs.getReader(null); - - }, label + ': calling getReader with invalid arguments should throw appropriate errors'); -} - -function templatedRSClosed(label, factory) { - test(() => {}, 'Running templatedRSClosed with ' + label); - - promise_test(() => { - - const rs = factory(); - const cancelPromise1 = rs.cancel(); - const cancelPromise2 = rs.cancel(); - - assert_not_equals(cancelPromise1, cancelPromise2, 'cancel() calls should return distinct promises'); - - return Promise.all([ - cancelPromise1.then(v => assert_equals(v, undefined, 'first cancel() call should fulfill with undefined')), - cancelPromise2.then(v => assert_equals(v, undefined, 'second cancel() call should fulfill with undefined')) - ]); - - }, label + ': cancel() should return a distinct fulfilled promise each time'); - - test(() => { - - const rs = factory(); - assert_false(rs.locked, 'locked getter should return false'); - - }, label + ': locked should be false'); - - test(() => { - - const rs = factory(); - rs.getReader(); // getReader() should not throw. - - }, label + ': getReader() should be OK'); - - test(() => { - - const rs = factory(); - - const reader = rs.getReader(); - reader.releaseLock(); - - const reader2 = rs.getReader(); // Getting a second reader should not throw. - reader2.releaseLock(); - - rs.getReader(); // Getting a third reader should not throw. - - }, label + ': should be able to acquire multiple readers if they are released in succession'); - - test(() => { - - const rs = factory(); - - rs.getReader(); - - assert_throws_js(TypeError, () => rs.getReader(), 'getting a second reader should throw'); - assert_throws_js(TypeError, () => rs.getReader(), 'getting a third reader should throw'); - - }, label + ': should not be able to acquire a second reader if we don\'t release the first one'); -} - -function templatedRSErrored(label, factory, error) { - test(() => {}, 'Running templatedRSErrored with ' + label); - - promise_test(t => { - - const rs = factory(); - const reader = rs.getReader(); - - return Promise.all([ - promise_rejects_exactly(t, error, reader.closed), - promise_rejects_exactly(t, error, reader.read()) - ]); - - }, label + ': getReader() should return a reader that acts errored'); - - promise_test(t => { - - const rs = factory(); - const reader = rs.getReader(); - - return Promise.all([ - promise_rejects_exactly(t, error, reader.read()), - promise_rejects_exactly(t, error, reader.read()), - promise_rejects_exactly(t, error, reader.closed) - ]); - - }, label + ': read() twice should give the error each time'); - - test(() => { - const rs = factory(); - - assert_false(rs.locked, 'locked getter should return false'); - }, label + ': locked should be false'); -} - -function templatedRSErroredSyncOnly(label, factory, error) { - test(() => {}, 'Running templatedRSErroredSyncOnly with ' + label); - - promise_test(t => { - - const rs = factory(); - rs.getReader().releaseLock(); - const reader = rs.getReader(); // Calling getReader() twice does not throw (the stream is not locked). - - return promise_rejects_exactly(t, error, reader.closed); - - }, label + ': should be able to obtain a second reader, with the correct closed promise'); - - test(() => { - - const rs = factory(); - rs.getReader(); - - assert_throws_js(TypeError, () => rs.getReader(), 'getting a second reader should throw a TypeError'); - assert_throws_js(TypeError, () => rs.getReader(), 'getting a third reader should throw a TypeError'); - - }, label + ': should not be able to obtain additional readers if we don\'t release the first lock'); - - promise_test(t => { - - const rs = factory(); - const cancelPromise1 = rs.cancel(); - const cancelPromise2 = rs.cancel(); - - assert_not_equals(cancelPromise1, cancelPromise2, 'cancel() calls should return distinct promises'); - - return Promise.all([ - promise_rejects_exactly(t, error, cancelPromise1), - promise_rejects_exactly(t, error, cancelPromise2) - ]); - - }, label + ': cancel() should return a distinct rejected promise each time'); - - promise_test(t => { - - const rs = factory(); - const reader = rs.getReader(); - const cancelPromise1 = reader.cancel(); - const cancelPromise2 = reader.cancel(); - - assert_not_equals(cancelPromise1, cancelPromise2, 'cancel() calls should return distinct promises'); - - return Promise.all([ - promise_rejects_exactly(t, error, cancelPromise1), - promise_rejects_exactly(t, error, cancelPromise2) - ]); - - }, label + ': reader cancel() should return a distinct rejected promise each time'); -} - -function templatedRSEmptyReader(label, factory) { - test(() => {}, 'Running templatedRSEmptyReader with ' + label); - - test(() => { - - const reader = factory().reader; - - assert_true('closed' in reader, 'has a closed property'); - assert_equals(typeof reader.closed.then, 'function', 'closed property is thenable'); - - assert_equals(typeof reader.cancel, 'function', 'has a cancel method'); - assert_equals(typeof reader.read, 'function', 'has a read method'); - assert_equals(typeof reader.releaseLock, 'function', 'has a releaseLock method'); - - }, label + ': instances have the correct methods and properties'); - - test(() => { - - const stream = factory().stream; - - assert_true(stream.locked, 'locked getter should return true'); - - }, label + ': locked should be true'); - - promise_test(t => { - - const reader = factory().reader; - - reader.read().then( - () => assert_unreached('read() should not fulfill'), - () => assert_unreached('read() should not reject') - ); - - return delay(500); - - }, label + ': read() should never settle'); - - promise_test(t => { - - const reader = factory().reader; - - reader.read().then( - () => assert_unreached('read() should not fulfill'), - () => assert_unreached('read() should not reject') - ); - - reader.read().then( - () => assert_unreached('read() should not fulfill'), - () => assert_unreached('read() should not reject') - ); - - return delay(500); - - }, label + ': two read()s should both never settle'); - - test(() => { - - const reader = factory().reader; - assert_not_equals(reader.read(), reader.read(), 'the promises returned should be distinct'); - - }, label + ': read() should return distinct promises each time'); - - test(() => { - - const stream = factory().stream; - assert_throws_js(TypeError, () => stream.getReader(), 'stream.getReader() should throw a TypeError'); - - }, label + ': getReader() again on the stream should fail'); - - promise_test(async t => { - - const streamAndReader = factory(); - const stream = streamAndReader.stream; - const reader = streamAndReader.reader; - - const read1 = reader.read(); - const read2 = reader.read(); - const closed = reader.closed; - - reader.releaseLock(); - - assert_false(stream.locked, 'the stream should be unlocked'); - - await Promise.all([ - promise_rejects_js(t, TypeError, read1, 'first read should reject'), - promise_rejects_js(t, TypeError, read2, 'second read should reject'), - promise_rejects_js(t, TypeError, closed, 'closed should reject') - ]); - - }, label + ': releasing the lock should reject all pending read requests'); - - promise_test(t => { - - const reader = factory().reader; - reader.releaseLock(); - - return Promise.all([ - promise_rejects_js(t, TypeError, reader.read()), - promise_rejects_js(t, TypeError, reader.read()) - ]); - - }, label + ': releasing the lock should cause further read() calls to reject with a TypeError'); - - promise_test(t => { - - const reader = factory().reader; - - const closedBefore = reader.closed; - reader.releaseLock(); - const closedAfter = reader.closed; - - assert_equals(closedBefore, closedAfter, 'the closed promise should not change identity'); - - return promise_rejects_js(t, TypeError, closedBefore); - - }, label + ': releasing the lock should cause closed calls to reject with a TypeError'); - - test(() => { - - const streamAndReader = factory(); - const stream = streamAndReader.stream; - const reader = streamAndReader.reader; - - reader.releaseLock(); - assert_false(stream.locked, 'locked getter should return false'); - - }, label + ': releasing the lock should cause locked to become false'); - - promise_test(() => { - - const reader = factory().reader; - reader.cancel(); - - return reader.read().then(r => { - assert_object_equals(r, { value: undefined, done: true }, 'read()ing from the reader should give a done result'); - }); - - }, label + ': canceling via the reader should cause the reader to act closed'); - - promise_test(t => { - - const stream = factory().stream; - return promise_rejects_js(t, TypeError, stream.cancel()); - - }, label + ': canceling via the stream should fail'); -} - -function templatedRSClosedReader(label, factory) { - test(() => {}, 'Running templatedRSClosedReader with ' + label); - - promise_test(() => { - - const reader = factory().reader; - - return reader.read().then(v => { - assert_object_equals(v, { value: undefined, done: true }, 'read() should fulfill correctly'); - }); - - }, label + ': read() should fulfill with { value: undefined, done: true }'); - - promise_test(() => { - - const reader = factory().reader; - - return Promise.all([ - reader.read().then(v => { - assert_object_equals(v, { value: undefined, done: true }, 'read() should fulfill correctly'); - }), - reader.read().then(v => { - assert_object_equals(v, { value: undefined, done: true }, 'read() should fulfill correctly'); - }) - ]); - - }, label + ': read() multiple times should fulfill with { value: undefined, done: true }'); - - promise_test(() => { - - const reader = factory().reader; - - return reader.read().then(() => reader.read()).then(v => { - assert_object_equals(v, { value: undefined, done: true }, 'read() should fulfill correctly'); - }); - - }, label + ': read() should work when used within another read() fulfill callback'); - - promise_test(() => { - - const reader = factory().reader; - - return reader.closed.then(v => assert_equals(v, undefined, 'reader closed should fulfill with undefined')); - - }, label + ': closed should fulfill with undefined'); - - promise_test(t => { - - const reader = factory().reader; - - const closedBefore = reader.closed; - reader.releaseLock(); - const closedAfter = reader.closed; - - assert_not_equals(closedBefore, closedAfter, 'the closed promise should change identity'); - - return Promise.all([ - closedBefore.then(v => assert_equals(v, undefined, 'reader.closed acquired before release should fulfill')), - promise_rejects_js(t, TypeError, closedAfter) - ]); - - }, label + ': releasing the lock should cause closed to reject and change identity'); - - promise_test(() => { - - const reader = factory().reader; - const cancelPromise1 = reader.cancel(); - const cancelPromise2 = reader.cancel(); - const closedReaderPromise = reader.closed; - - assert_not_equals(cancelPromise1, cancelPromise2, 'cancel() calls should return distinct promises'); - assert_not_equals(cancelPromise1, closedReaderPromise, 'cancel() promise 1 should be distinct from reader.closed'); - assert_not_equals(cancelPromise2, closedReaderPromise, 'cancel() promise 2 should be distinct from reader.closed'); - - return Promise.all([ - cancelPromise1.then(v => assert_equals(v, undefined, 'first cancel() should fulfill with undefined')), - cancelPromise2.then(v => assert_equals(v, undefined, 'second cancel() should fulfill with undefined')) - ]); - - }, label + ': cancel() should return a distinct fulfilled promise each time'); -} - -function templatedRSErroredReader(label, factory, error) { - test(() => {}, 'Running templatedRSErroredReader with ' + label); - - promise_test(t => { - - const reader = factory().reader; - return promise_rejects_exactly(t, error, reader.closed); - - }, label + ': closed should reject with the error'); - - promise_test(t => { - - const reader = factory().reader; - const closedBefore = reader.closed; - - return promise_rejects_exactly(t, error, closedBefore).then(() => { - reader.releaseLock(); - - const closedAfter = reader.closed; - assert_not_equals(closedBefore, closedAfter, 'the closed promise should change identity'); - - return promise_rejects_js(t, TypeError, closedAfter); - }); - - }, label + ': releasing the lock should cause closed to reject and change identity'); - - promise_test(t => { - - const reader = factory().reader; - return promise_rejects_exactly(t, error, reader.read()); - - }, label + ': read() should reject with the error'); -} - -function templatedRSTwoChunksOpenReader(label, factory, chunks) { - test(() => {}, 'Running templatedRSTwoChunksOpenReader with ' + label); - - promise_test(() => { - - const reader = factory().reader; - - return Promise.all([ - reader.read().then(r => { - assert_object_equals(r, { value: chunks[0], done: false }, 'first result should be correct'); - }), - reader.read().then(r => { - assert_object_equals(r, { value: chunks[1], done: false }, 'second result should be correct'); - }) - ]); - - }, label + ': calling read() twice without waiting will eventually give both chunks (sequential)'); - - promise_test(() => { - - const reader = factory().reader; - - return reader.read().then(r => { - assert_object_equals(r, { value: chunks[0], done: false }, 'first result should be correct'); - - return reader.read().then(r2 => { - assert_object_equals(r2, { value: chunks[1], done: false }, 'second result should be correct'); - }); - }); - - }, label + ': calling read() twice without waiting will eventually give both chunks (nested)'); - - test(() => { - - const reader = factory().reader; - assert_not_equals(reader.read(), reader.read(), 'the promises returned should be distinct'); - - }, label + ': read() should return distinct promises each time'); - - promise_test(() => { - - const reader = factory().reader; - - const promise1 = reader.closed.then(v => { - assert_equals(v, undefined, 'reader closed should fulfill with undefined'); - }); - - const promise2 = reader.read().then(r => { - assert_object_equals(r, { value: chunks[0], done: false }, - 'promise returned before cancellation should fulfill with a chunk'); - }); - - reader.cancel(); - - const promise3 = reader.read().then(r => { - assert_object_equals(r, { value: undefined, done: true }, - 'promise returned after cancellation should fulfill with an end-of-stream signal'); - }); - - return Promise.all([promise1, promise2, promise3]); - - }, label + ': cancel() after a read() should still give that single read result'); -} - -function templatedRSTwoChunksClosedReader(label, factory, chunks) { - test(() => {}, 'Running templatedRSTwoChunksClosedReader with ' + label); - - promise_test(() => { - - const reader = factory().reader; - - return Promise.all([ - reader.read().then(r => { - assert_object_equals(r, { value: chunks[0], done: false }, 'first result should be correct'); - }), - reader.read().then(r => { - assert_object_equals(r, { value: chunks[1], done: false }, 'second result should be correct'); - }), - reader.read().then(r => { - assert_object_equals(r, { value: undefined, done: true }, 'third result should be correct'); - }) - ]); - - }, label + ': third read(), without waiting, should give { value: undefined, done: true } (sequential)'); - - promise_test(() => { - - const reader = factory().reader; - - return reader.read().then(r => { - assert_object_equals(r, { value: chunks[0], done: false }, 'first result should be correct'); - - return reader.read().then(r2 => { - assert_object_equals(r2, { value: chunks[1], done: false }, 'second result should be correct'); - - return reader.read().then(r3 => { - assert_object_equals(r3, { value: undefined, done: true }, 'third result should be correct'); - }); - }); - }); - - }, label + ': third read(), without waiting, should give { value: undefined, done: true } (nested)'); - - promise_test(() => { - - const streamAndReader = factory(); - const stream = streamAndReader.stream; - const reader = streamAndReader.reader; - - assert_true(stream.locked, 'stream should start locked'); - - const promise = reader.closed.then(v => { - assert_equals(v, undefined, 'reader closed should fulfill with undefined'); - assert_true(stream.locked, 'stream should remain locked'); - }); - - reader.read(); - reader.read(); - - return promise; - - }, label + - ': draining the stream via read() should cause the reader closed promise to fulfill, but locked stays true'); - - promise_test(() => { - - const streamAndReader = factory(); - const stream = streamAndReader.stream; - const reader = streamAndReader.reader; - - const promise = reader.closed.then(() => { - assert_true(stream.locked, 'the stream should start locked'); - reader.releaseLock(); // Releasing the lock after reader closed should not throw. - assert_false(stream.locked, 'the stream should end unlocked'); - }); - - reader.read(); - reader.read(); - - return promise; - - }, label + ': releasing the lock after the stream is closed should cause locked to become false'); - - promise_test(t => { - - const reader = factory().reader; - - reader.releaseLock(); - - return Promise.all([ - promise_rejects_js(t, TypeError, reader.read()), - promise_rejects_js(t, TypeError, reader.read()), - promise_rejects_js(t, TypeError, reader.read()) - ]); - - }, label + ': releasing the lock should cause further read() calls to reject with a TypeError'); - - promise_test(() => { - - const streamAndReader = factory(); - const stream = streamAndReader.stream; - const reader = streamAndReader.reader; - - const readerClosed = reader.closed; - - assert_equals(reader.closed, readerClosed, 'accessing reader.closed twice in succession gives the same value'); - - const promise = reader.read().then(() => { - assert_equals(reader.closed, readerClosed, 'reader.closed is the same after read() fulfills'); - - reader.releaseLock(); - - assert_equals(reader.closed, readerClosed, 'reader.closed is the same after releasing the lock'); - - const newReader = stream.getReader(); - return newReader.read(); - }); - - assert_equals(reader.closed, readerClosed, 'reader.closed is the same after calling read()'); - - return promise; - - }, label + ': reader\'s closed property always returns the same promise'); -} - -// FIXME: Uncomment once we add that support -// function templatedRSTeeCancel(label, factory) { -// test(() => {}, `Running templatedRSTeeCancel with ${label}`); -// -// promise_test(async () => { -// -// const reason1 = new Error('We\'re wanted men.'); -// const reason2 = new Error('I have the death sentence on twelve systems.'); -// -// let resolve; -// const promise = new Promise(r => resolve = r); -// const rs = factory({ -// cancel(reason) { -// assert_array_equals(reason, [reason1, reason2], -// 'the cancel reason should be an array containing those from the branches'); -// resolve(); -// } -// }); -// -// const [branch1, branch2] = rs.tee(); -// await Promise.all([ -// branch1.cancel(reason1), -// branch2.cancel(reason2), -// promise -// ]); -// -// }, `${label}: canceling both branches should aggregate the cancel reasons into an array`); -// -// promise_test(async () => { -// -// const reason1 = new Error('This little one\'s not worth the effort.'); -// const reason2 = new Error('Come, let me get you something.'); -// -// let resolve; -// const promise = new Promise(r => resolve = r); -// const rs = factory({ -// cancel(reason) { -// assert_array_equals(reason, [reason1, reason2], -// 'the cancel reason should be an array containing those from the branches'); -// resolve(); -// } -// }); -// -// const [branch1, branch2] = rs.tee(); -// await Promise.all([ -// branch2.cancel(reason2), -// branch1.cancel(reason1), -// promise -// ]); -// -// }, `${label}: canceling both branches in reverse order should aggregate the cancel reasons into an array`); -// -// promise_test(async t => { -// -// const theError = { name: 'I\'ll be careful.' }; -// const rs = factory({ -// cancel() { -// throw theError; -// } -// }); -// -// const [branch1, branch2] = rs.tee(); -// await Promise.all([ -// promise_rejects_exactly(t, theError, branch1.cancel()), -// promise_rejects_exactly(t, theError, branch2.cancel()) -// ]); -// -// }, `${label}: failing to cancel the original stream should cause cancel() to reject on branches`); -// -// promise_test(async t => { -// -// const theError = { name: 'You just watch yourself!' }; -// let controller; -// const stream = factory({ -// start(c) { -// controller = c; -// } -// }); -// -// const [branch1, branch2] = stream.tee(); -// controller.error(theError); -// -// await Promise.all([ -// promise_rejects_exactly(t, theError, branch1.cancel()), -// promise_rejects_exactly(t, theError, branch2.cancel()) -// ]); -// -// }, `${label}: erroring a teed stream should properly handle canceled branches`); -// -// } \ No newline at end of file diff --git a/js/modulestest/wptutils/rs-utils.js b/js/modulestest/wptutils/rs-utils.js deleted file mode 100644 index aee32deee93..00000000000 --- a/js/modulestest/wptutils/rs-utils.js +++ /dev/null @@ -1,178 +0,0 @@ -// Original source file: https://github.com/web-platform-tests/wpt/blob/7b29ee36cc22bdad06b4f98df73358ca959fe0a7/streams/resources/rs-utils.js -// This test utilities file have been slightly modified to make it fit our test suite runner/runtime. -class RandomPushSource { - constructor(toPush) { - this.pushed = 0; - this.toPush = toPush; - this.started = false; - this.paused = false; - this.closed = false; - - this._intervalHandle = null; - } - - readStart() { - if (this.closed) { - return; - } - - if (!this.started) { - this._intervalHandle = setInterval(writeChunk, 2); - this.started = true; - } - - if (this.paused) { - this._intervalHandle = setInterval(writeChunk, 2); - this.paused = false; - } - - const source = this; - - function writeChunk() { - if (source.paused) { - return; - } - - source.pushed++; - - if (source.toPush > 0 && source.pushed > source.toPush) { - if (source._intervalHandle) { - clearInterval(source._intervalHandle); - source._intervalHandle = undefined; - } - source.closed = true; - source.onend(); - } else { - source.ondata(randomChunk(128)); - } - } - } - - readStop() { - if (this.paused) { - return; - } - - if (this.started) { - this.paused = true; - clearInterval(this._intervalHandle); - this._intervalHandle = undefined; - } else { - throw new Error('Can\'t pause reading an unstarted source.'); - } - } -} - -function randomChunk(size) { - let chunk = ''; - - for (let i = 0; i < size; ++i) { - // Add a random character from the basic printable ASCII set. - chunk += String.fromCharCode(Math.round(Math.random() * 84) + 32); - } - - return chunk; -} - -function readableStreamToArray(readable, reader) { - if (reader === undefined) { - reader = readable.getReader(); - } - - const chunks = []; - - return pump(); - - function pump() { - return reader.read().then(result => { - if (result.done) { - return chunks; - } - - chunks.push(result.value); - return pump(); - }); - } -} - -class SequentialPullSource { - constructor(limit, options) { - const async = options && options.async; - - this.current = 0; - this.limit = limit; - this.opened = false; - this.closed = false; - - this._exec = f => f(); - if (async) { - this._exec = f => step_timeout(f, 0); - } - } - - open(cb) { - this._exec(() => { - this.opened = true; - cb(); - }); - } - - read(cb) { - this._exec(() => { - if (++this.current <= this.limit) { - cb(null, false, this.current); - } else { - cb(null, true, null); - } - }); - } - - close(cb) { - this._exec(() => { - this.closed = true; - cb(); - }); - } -} - -function sequentialReadableStream(limit, options) { - const sequentialSource = new SequentialPullSource(limit, options); - - const stream = new ReadableStream({ - start() { - return new Promise((resolve, reject) => { - sequentialSource.open(err => { - if (err) { - reject(err); - } - resolve(); - }); - }); - }, - - pull(c) { - return new Promise((resolve, reject) => { - sequentialSource.read((err, done, chunk) => { - if (err) { - reject(err); - } else if (done) { - sequentialSource.close(err2 => { - if (err2) { - reject(err2); - } - c.close(); - resolve(); - }); - } else { - c.enqueue(chunk); - resolve(); - } - }); - }); - } - }); - - stream.source = sequentialSource; - - return stream; -} \ No newline at end of file diff --git a/js/modulestest/wptutils/test-utils.js b/js/modulestest/wptutils/test-utils.js deleted file mode 100644 index b2b058555bd..00000000000 --- a/js/modulestest/wptutils/test-utils.js +++ /dev/null @@ -1,18 +0,0 @@ -// Original source file: https://github.com/web-platform-tests/wpt/blob/7eaf605c38d80377c717828376deabad86b702b2/streams/resources/test-utils.js -'use strict'; - -function delay(ms) { - return new Promise(resolve => step_timeout(resolve, ms)); -} - -// For tests which verify that the implementation doesn't do something it shouldn't, it's better not to use a -// timeout. Instead, assume that any reasonable implementation is going to finish work after 2 times around the event -// loop, and use flushAsyncEvents().then(() => assert_array_equals(...)); -// Some tests include promise resolutions which may mean the test code takes a couple of event loop visits itself. So go -// around an extra 2 times to avoid complicating those tests. -function flushAsyncEvents() { - return delay(0) - .then(() => delay(0)) - .then(() => delay(0)) - .then(() => delay(0)); -} \ No newline at end of file diff --git a/js/modulestest/wptutils/testharness.js b/js/modulestest/wptutils/testharness.js deleted file mode 100644 index fccbe3a43f0..00000000000 --- a/js/modulestest/wptutils/testharness.js +++ /dev/null @@ -1,708 +0,0 @@ -// This file contains a partial adaptation of the testharness.js implementation from -// the W3C WebCrypto API test suite. It is not intended to be a complete -// implementation, but rather a minimal set of functions to support the -// tests for this extension. -// -// Some of the function have been modified to support the k6 javascript runtime, -// and to limit its dependency to the rest of the W3C WebCrypto API test suite internal -// codebase. -// -// The original testharness.js implementation is available at: -// https://github.com/web-platform-tests/wpt/blob/3a3453c62176c97ab51cd492553c2dacd24366b1/resources/testharness.js - -/** - * Utility functions. - */ -function test(func, name) { - try { - func(); - } catch (e) { - throw `${name} failed - ${e}`; - } -} - -function promise_test(func, name) { - func().catch((e) => { - throw `${name} failed - ${e}`; - }); -} - -/** - * Make a copy of a Promise in the current realm. - * - * @param {Promise} promise the given promise that may be from a different - * realm - * @returns {Promise} - * - * An arbitrary promise provided by the caller may have originated - * in another frame that have since navigated away, rendering the - * frame's document inactive. Such a promise cannot be used with - * `await` or Promise.resolve(), as microtasks associated with it - * may be prevented from being run. See `issue - * 5319`_ for a - * particular case. - * - * In functions we define here, there is an expectation from the caller - * that the promise is from the current realm, that can always be used with - * `await`, etc. We therefore create a new promise in this realm that - * inherit the value and status from the given promise. - */ - -function bring_promise_to_current_realm(promise) { - return new Promise(promise.then.bind(promise)); -} - -/** - * @class - * Exception type that represents a failing assert. - * - * @param {string} message - Error message. - */ -function AssertionError(message) { - if (typeof message == "string") { - message = sanitize_unpaired_surrogates(message); - } - this.message = message; - this.stack = get_stack(); -} - -AssertionError.prototype = Object.create(Error.prototype); - -function assert(expected_true, function_name, description, error, substitutions) { - if (expected_true !== true) { - var msg = make_message(function_name, description, - error, substitutions); - throw new AssertionError(msg); - } -} - - -// NOTE: This is a simplified version of the original implementation -// found at: https://github.com/web-platform-tests/wpt/blob/e955fbc72b5a98e1c2dc6a6c1a048886c8a99785/resources/testharness.js#L4615 -function make_message(function_name, description, error, substitutions) { - // for (var p in substitutions) { - // if (substitutions.hasOwnProperty(p)) { - // substitutions[p] = format_value(substitutions[p]); - // } - // } - var node_form = substitute(["{text}", "${function_name}: ${description}" + error], - merge({ - function_name: function_name, - description: (description ? description + " " : "") - }, - substitutions)); - return node_form.slice(1).join(""); -} - -function is_single_node(template) { - return typeof template[0] === "string"; -} - -function substitute(template, substitutions) { - if (typeof template === "function") { - var replacement = template(substitutions); - if (!replacement) { - return null; - } - - return substitute(replacement, substitutions); - } - - if (is_single_node(template)) { - return substitute_single(template, substitutions); - } - - return filter(map(template, function (x) { - return substitute(x, substitutions); - }), function (x) { - return x !== null; - }); -} - -function substitute_single(template, substitutions) { - var substitution_re = /\$\{([^ }]*)\}/g; - - function do_substitution(input) { - var components = input.split(substitution_re); - var rv = []; - for (var i = 0; i < components.length; i += 2) { - rv.push(components[i]); - if (components[i + 1]) { - rv.push(String(substitutions[components[i + 1]])); - } - } - return rv; - } - - function substitute_attrs(attrs, rv) { - rv[1] = {}; - for (var name in template[1]) { - if (attrs.hasOwnProperty(name)) { - var new_name = do_substitution(name).join(""); - var new_value = do_substitution(attrs[name]).join(""); - rv[1][new_name] = new_value; - } - } - } - - function substitute_children(children, rv) { - for (var i = 0; i < children.length; i++) { - if (children[i] instanceof Object) { - var replacement = substitute(children[i], substitutions); - if (replacement !== null) { - if (is_single_node(replacement)) { - rv.push(replacement); - } else { - extend(rv, replacement); - } - } - } else { - extend(rv, do_substitution(String(children[i]))); - } - } - return rv; - } - - var rv = []; - rv.push(do_substitution(String(template[0])).join("")); - - if (template[0] === "{text}") { - substitute_children(template.slice(1), rv); - } else { - substitute_attrs(template[1], rv); - substitute_children(template.slice(2), rv); - } - - return rv; -} - -function filter(array, callable, thisObj) { - var rv = []; - for (var i = 0; i < array.length; i++) { - if (array.hasOwnProperty(i)) { - var pass = callable.call(thisObj, array[i], i, array); - if (pass) { - rv.push(array[i]); - } - } - } - return rv; -} - -function map(array, callable, thisObj) { - var rv = []; - rv.length = array.length; - for (var i = 0; i < array.length; i++) { - if (array.hasOwnProperty(i)) { - rv[i] = callable.call(thisObj, array[i], i, array); - } - } - return rv; -} - -function extend(array, items) { - Array.prototype.push.apply(array, items); -} - -function forEach(array, callback, thisObj) { - for (var i = 0; i < array.length; i++) { - if (array.hasOwnProperty(i)) { - callback.call(thisObj, array[i], i, array); - } - } -} - -function merge(a, b) { - var rv = {}; - var p; - for (p in a) { - rv[p] = a[p]; - } - for (p in b) { - rv[p] = b[p]; - } - return rv; -} - -/** - * Assert that ``actual`` is the same value as ``expected``. - * - * For objects this compares by cobject identity; for primitives - * this distinguishes between 0 and -0, and has correct handling - * of NaN. - * - * @param {Any} actual - Test value. - * @param {Any} expected - Expected value. - * @param {string} [description] - Description of the condition being tested. - */ -function assert_equals(actual, expected, description) { - if (actual !== expected) { - throw `assert_equals ${description} expected (${typeof expected}) ${expected} but got (${typeof actual}) ${actual}`; - } -} - -/** - * Assert that ``actual`` is not the same value as ``expected``. - * - * Comparison is as for :js:func:`assert_equals`. - * - * @param {Any} actual - Test value. - * @param {Any} expected - The value ``actual`` is expected to be different to. - * @param {string} [description] - Description of the condition being tested. - */ -function assert_not_equals(actual, expected, description) { - if (actual === expected) { - throw `assert_not_equals ${description} got disallowed value ${actual}`; - } -} - -/** - * Assert that ``actual`` is strictly true - * - * @param {Any} actual - Value that is asserted to be true - * @param {string} [description] - Description of the condition being tested - */ -function assert_true(actual, description) { - if (!actual) { - throw `assert_true ${description} expected true got ${actual}`; - } -} - -/** - * Assert that ``actual`` is strictly false - * - * @param {Any} actual - Value that is asserted to be false - * @param {string} [description] - Description of the condition being tested - */ -function assert_false(actual, description) { - if (actual) { - throw `assert_true ${description} expected false got ${actual}`; - } -} - -/** - * Assert that ``expected`` is an array and ``actual`` is one of the members. - * This is implemented using ``indexOf``, so doesn't handle NaN or ±0 correctly. - * - * @param {Any} actual - Test value. - * @param {Array} expected - An array that ``actual`` is expected to - * be a member of. - * @param {string} [description] - Description of the condition being tested. - */ -function assert_in_array(actual, expected, description) { - if (expected.indexOf(actual) === -1) { - throw `assert_in_array ${description} value ${actual} not in array ${expected}`; - } -} - -/** - * Asserts if called. Used to ensure that a specific codepath is - * not taken e.g. that an error event isn't fired. - * - * @param {string} [description] - Description of the condition being tested. - */ -function assert_unreached(description) { - throw `reached unreachable code, reason: ${description}` -} - -/** - * Assert that ``actual`` and ``expected`` are both arrays, and that the array properties of - * ``actual`` and ``expected`` are all the same value (as for :js:func:`assert_equals`). - * - * @param {Array} actual - Test array. - * @param {Array} expected - Array that is expected to contain the same values as ``actual``. - * @param {string} [description] - Description of the condition being tested. - */ -function assert_array_equals(actual, expected, description) { - if (typeof actual !== "object" || actual === null || !("length" in actual)) { - throw `assert_array_equals ${description} value is ${actual}, expected array`; - } - - if (actual.length !== expected.length) { - throw `assert_array_equals ${description} lengths differ, expected array ${expected} length ${expected.length}, got ${actual} length ${actual.length}`; - } - - for (var i = 0; i < actual.length; i++) { - if (actual.hasOwnProperty(i) !== expected.hasOwnProperty(i)) { - throw `assert_array_equals ${description} expected property ${i} to be ${expected.hasOwnProperty(i)} but was ${actual.hasOwnProperty(i)} (expected array ${expected} got ${actual})`; - } - - if (!same_value(expected[i], actual[i])) { - throw `assert_array_equals ${description} expected property ${i} to be ${expected[i]} but got ${actual[i]} (expected array ${expected} got ${actual})`; - } - } -} - -/** - * Assert that ``actual`` is a number greater than ``expected``. - * - * @param {number} actual - Test value. - * @param {number} expected - Number that ``actual`` must be greater than. - * @param {string} [description] - Description of the condition being tested. - */ -function assert_greater_than(actual, expected, description) -{ - /* - * Test if a primitive number is greater than another - */ - assert(typeof actual === "number", - "assert_greater_than", description, - "expected a number but got a ${type_actual}", - {type_actual:typeof actual}); - - assert(actual > expected, - "assert_greater_than", description, - "expected a number greater than ${expected} but got ${actual}", - {expected:expected, actual:actual}); -} - -/** - * Assert the provided value is thrown. - * - * @param {value} exception The expected exception. - * @param {Function} func Function which should throw. - * @param {string} [description] Error description for the case that the error is not thrown. - */ -function assert_throws_exactly(exception, func, description) { - assert_throws_exactly_impl(exception, func, description, - "assert_throws_exactly"); -} - -/** - * Like assert_throws_exactly but allows specifying the assertion type - * (assert_throws_exactly or promise_rejects_exactly, in practice). - */ -function assert_throws_exactly_impl(exception, func, description, - assertion_type) { - try { - func.call(this); - assert(false, assertion_type, description, - "${func} did not throw", {func: func}); - } catch (e) { - if (e instanceof AssertionError) { - throw e; - } - - assert(same_value(e, exception), assertion_type, description, - "${func} threw ${e} but we expected it to throw ${exception}", - {func: func, e: e, exception: exception}); - } -} - -/** - * Assert that a Promise is rejected with the provided value. - * - * @param {Test} test - the `Test` to use for the assertion. - * @param {Any} exception - The expected value of the rejected promise. - * @param {Promise} promise - The promise that's expected to - * reject. - * @param {string} [description] Error message to add to assert in case of - * failure. - */ -function promise_rejects_exactly(test, exception, promise, description) { - return promise.then( - () => { - throw new Error("Should have rejected: " + description); - }, - (e) => { - assert_throws_exactly_impl(exception, function () { - throw e - }, - description, "promise_rejects_exactly"); - } - ); -} - -/** - * Assert that a Promise is rejected with the right ECMAScript exception. - * - * @param {Test} test - the `Test` to use for the assertion. - * @param {Function} constructor - The expected exception constructor. - * @param {Promise} promise - The promise that's expected to - * reject with the given exception. - * @param {string} [description] Error message to add to assert in case of - * failure. - */ -function promise_rejects_js(test, constructor, promise, description) { - return bring_promise_to_current_realm(promise) - .then(() => {assert_unreached("Should have rejected: " + description)}) - .catch(function(e) { - assert_throws_js_impl(constructor, function() { throw e }, - description, "promise_rejects_js"); - }); -} - -/** - * Assert a JS Error with the expected constructor is thrown. - * - * @param {object} constructor The expected exception constructor. - * @param {Function} func Function which should throw. - * @param {string} [description] Error description for the case that the error is not thrown. - */ -function assert_throws_js(constructor, func, description) { - assert_throws_js_impl(constructor, func, description, - "assert_throws_js"); -} - -/** - * Like assert_throws_js but allows specifying the assertion type - * (assert_throws_js or promise_rejects_js, in practice). - */ -function assert_throws_js_impl(constructor, func, description, - assertion_type) { - try { - func.call(this); - assert(false, assertion_type, description, - "${func} did not throw", {func: func}); - } catch (e) { - if (e instanceof AssertionError) { - throw e; - } - - // Basic sanity-checks on the thrown exception. - assert(typeof e === "object", - assertion_type, description, - "${func} threw ${e} with type ${type}, not an object", - {func: func, e: e, type: typeof e}); - - assert(e !== null, - assertion_type, description, - "${func} threw null, not an object", - {func: func}); - - // Note @oleiade: As k6 does not throw error objects that match the Javascript - // standard errors and their associated expectations and properties, we cannot - // rely on the WPT assertions to be true. - // - // Instead, we check that the error object has the shape we give it when we throw it. - // Namely, that it has a name property that matches the name of the expected constructor. - - assert('name' in e, - assertion_type, description, - "${func} threw ${e} without a name property", - {func: func, e: e}); - - assert(e.name === constructor.name, - assertion_type, description, - "${func} threw ${e} with name ${e.name}, not ${constructor.name}", - {func: func, e: e, constructor: constructor}); - - // Note @oleiade: We deactivated the following assertions in favor of our own - // as mentioned above. - - // Basic sanity-check on the passed-in constructor - // assert(typeof constructor == "function", - // assertion_type, description, - // "${constructor} is not a constructor", - // {constructor:constructor}); - // var obj = constructor; - // while (obj) { - // if (typeof obj === "function" && - // obj.name === "Error") { - // break; - // } - // obj = Object.getPrototypeOf(obj); - // } - // assert(obj != null, - // assertion_type, description, - // "${constructor} is not an Error subtype", - // {constructor:constructor}); - // - // // And checking that our exception is reasonable - // assert(e.constructor === constructor && - // e.name === constructor.name, - // assertion_type, description, - // "${func} threw ${actual} (${actual_name}) expected instance of ${expected} (${expected_name})", - // {func:func, actual:e, actual_name:e.name, - // expected:constructor, - // expected_name:constructor.name}); - } -} - -function same_value(x, y) { - if (y !== y) { - //NaN case - return x !== x; - } - if (x === 0 && y === 0) { - //Distinguish +0 and -0 - return 1 / x === 1 / y; - } - // Note @joanlopez: We cannot rely on the WPT assertions implementation, because - // k6 does not throw error objects that match the JavaScript standard errors and - // their associated expectations and properties. - if (isObject(x) && isObject(y)) { - return areObjectsEquivalent(x, y); - } - return x === y; -} - -function isObject(object) { - return typeof object === 'object' && object !== null; -} - -function areObjectsEquivalent(obj1, obj2) { - const obj1Keys = Object.keys(obj1); - const obj2Keys = Object.keys(obj2); - - if (obj1Keys.length !== obj2Keys.length) { - return false; // They have different numbers of keys - } - - for (let key of obj1Keys) { - const val1 = obj1[key]; - const val2 = obj2[key]; - const areObjects = isObject(val1) && isObject(val2); - if ( - (areObjects && !areObjectsEquivalent(val1, val2)) || - (!areObjects && val1 !== val2) - ) { - return false; // Either the values are not equal or, if objects, not equivalent - } - } - return true; // Everything matched -} - -// This function was deprecated in July of 2015. -// See https://github.com/web-platform-tests/wpt/issues/2033 -/** - * @deprecated - * Recursively compare two objects for equality. - * - * See `Issue 2033 - * `_ for - * more information. - * - * @param {Object} actual - Test value. - * @param {Object} expected - Expected value. - * @param {string} [description] - Description of the condition being tested. - */ -function assert_object_equals(actual, expected, description) -{ - assert(typeof actual === "object" && actual !== null, "assert_object_equals", description, - "value is ${actual}, expected object", - {actual: actual}); - //This needs to be improved a great deal - function check_equal(actual, expected, stack) - { - stack.push(actual); - - var p; - for (p in actual) { - assert(expected.hasOwnProperty(p), "assert_object_equals", description, - "unexpected property ${p}", {p:p}); - - if (typeof actual[p] === "object" && actual[p] !== null) { - if (stack.indexOf(actual[p]) === -1) { - check_equal(actual[p], expected[p], stack); - } - } else { - assert(same_value(actual[p], expected[p]), "assert_object_equals", description, - "property ${p} expected ${expected} got ${actual}", - {p:p, expected:expected[p], actual:actual[p]}); - } - } - for (p in expected) { - assert(actual.hasOwnProperty(p), - "assert_object_equals", description, - "expected property ${p} missing", {p:p}); - } - stack.pop(); - } - check_equal(actual, expected, []); -} - -function code_unit_str(char) { - return 'U+' + char.charCodeAt(0).toString(16); -} - -function sanitize_unpaired_surrogates(str) { - return str.replace( - /([\ud800-\udbff]+)(?![\udc00-\udfff])|(^|[^\ud800-\udbff])([\udc00-\udfff]+)/g, - function (_, low, prefix, high) { - var output = prefix || ""; // prefix may be undefined - var string = low || high; // only one of these alternates can match - for (var i = 0; i < string.length; i++) { - output += code_unit_str(string[i]); - } - return output; - }); -} - -const get_stack = function () { - var stack = new Error().stack; - - // 'Error.stack' is not supported in all browsers/versions - if (!stack) { - return "(Stack trace unavailable)"; - } - - var lines = stack.split("\n"); - - // Create a pattern to match stack frames originating within testharness.js. These include the - // script URL, followed by the line/col (e.g., '/resources/testharness.js:120:21'). - // Escape the URL per http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript - // in case it contains RegExp characters. - // NOTE @oleiade: We explicitly bypass the get_script_url operation as it's specific to the - // web platform test suite and enforce the use of an empty string instead. - // var script_url = get_script_url(); - var script_url = ''; - var re_text = script_url ? script_url.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') : "\\btestharness.js"; - var re = new RegExp(re_text + ":\\d+:\\d+"); - - // Some browsers include a preamble that specifies the type of the error object. Skip this by - // advancing until we find the first stack frame originating from testharness.js. - var i = 0; - while (!re.test(lines[i]) && i < lines.length) { - i++; - } - - // Then skip the top frames originating from testharness.js to begin the stack at the test code. - while (re.test(lines[i]) && i < lines.length) { - i++; - } - - // Paranoid check that we didn't skip all frames. If so, return the original stack unmodified. - if (i >= lines.length) { - return stack; - } - - return lines.slice(i).join("\n"); -} - -// Internal helper function to provide timeout-like functionality in -// environments where there is no setTimeout(). (No timeout ID or -// clearTimeout().) -function fake_set_timeout(callback, delay) { - var p = Promise.resolve(); - var start = Date.now(); - var end = start + delay; - - function check() { - if ((end - Date.now()) > 0) { - p.then(check); - } else { - callback(); - } - } - - p.then(check); -} - - -/** - * Global version of :js:func:`Test.step_timeout` for use in single page tests. - * - * @param {Function} func - Function to run after the timeout - * @param {number} timeout - Time in ms to wait before running the - * test step. The actual wait time is ``timeout`` x - * ``timeout_multiplier`` (NOTE: Set to 1 for simplicity). - */ -function step_timeout(func, timeout) { - var outer_this = this; - var args = Array.prototype.slice.call(arguments, 2); - var local_set_timeout = typeof this.setTimeout === "undefined" ? fake_set_timeout : setTimeout; - return local_set_timeout(function () { - func.apply(outer_this, args); - }, timeout); -} \ No newline at end of file