| "use strict"; |
| var __create = Object.create; |
| var __defProp = Object.defineProperty; |
| var __getOwnPropDesc = Object.getOwnPropertyDescriptor; |
| var __getOwnPropNames = Object.getOwnPropertyNames; |
| var __getProtoOf = Object.getPrototypeOf; |
| var __hasOwnProp = Object.prototype.hasOwnProperty; |
| var __commonJS = (cb, mod) => function __require() { |
| return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; |
| }; |
| var __copyProps = (to, from, except, desc) => { |
| if (from && typeof from === "object" || typeof from === "function") { |
| for (let key of __getOwnPropNames(from)) |
| if (!__hasOwnProp.call(to, key) && key !== except) |
| __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); |
| } |
| return to; |
| }; |
| var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( |
| |
| |
| |
| |
| isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, |
| mod |
| )); |
|
|
| |
| var require_pend = __commonJS({ |
| "packages/utils/third_party/yauzl/pend.js"(exports2, module2) { |
| "use strict"; |
| module2.exports = Pend; |
| function Pend() { |
| this.pending = 0; |
| this.max = Infinity; |
| this.listeners = []; |
| this.waiting = []; |
| this.error = null; |
| } |
| Pend.prototype.go = function(fn) { |
| if (this.pending < this.max) { |
| pendGo(this, fn); |
| } else { |
| this.waiting.push(fn); |
| } |
| }; |
| Pend.prototype.wait = function(cb) { |
| if (this.pending === 0) { |
| cb(this.error); |
| } else { |
| this.listeners.push(cb); |
| } |
| }; |
| Pend.prototype.hold = function() { |
| return pendHold(this); |
| }; |
| function pendHold(self) { |
| self.pending += 1; |
| var called = false; |
| return onCb; |
| function onCb(err) { |
| if (called) throw new Error("callback called twice"); |
| called = true; |
| self.error = self.error || err; |
| self.pending -= 1; |
| if (self.waiting.length > 0 && self.pending < self.max) { |
| pendGo(self, self.waiting.shift()); |
| } else if (self.pending === 0) { |
| var listeners = self.listeners; |
| self.listeners = []; |
| listeners.forEach(cbListener); |
| } |
| } |
| function cbListener(listener) { |
| listener(self.error); |
| } |
| } |
| function pendGo(self, fn) { |
| fn(pendHold(self)); |
| } |
| } |
| }); |
|
|
| |
| var require_fd_slicer = __commonJS({ |
| "packages/utils/third_party/yauzl/fd-slicer.js"(exports2) { |
| "use strict"; |
| var fs4 = require("fs"); |
| var util2 = require("util"); |
| var stream = require("stream"); |
| var Readable = stream.Readable; |
| var Writable = stream.Writable; |
| var PassThrough = stream.PassThrough; |
| var Pend = require_pend(); |
| var EventEmitter = require("events").EventEmitter; |
| exports2.createFromBuffer = createFromBuffer; |
| exports2.createFromFd = createFromFd; |
| exports2.BufferSlicer = BufferSlicer; |
| exports2.FdSlicer = FdSlicer; |
| util2.inherits(FdSlicer, EventEmitter); |
| function FdSlicer(fd, options2) { |
| options2 = options2 || {}; |
| EventEmitter.call(this); |
| this.fd = fd; |
| this.pend = new Pend(); |
| this.pend.max = 1; |
| this.refCount = 0; |
| this.autoClose = !!options2.autoClose; |
| } |
| FdSlicer.prototype.read = function(buffer, offset, length, position, callback) { |
| var self = this; |
| self.pend.go(function(cb) { |
| fs4.read(self.fd, buffer, offset, length, position, function(err, bytesRead, buffer2) { |
| cb(); |
| callback(err, bytesRead, buffer2); |
| }); |
| }); |
| }; |
| FdSlicer.prototype.write = function(buffer, offset, length, position, callback) { |
| var self = this; |
| self.pend.go(function(cb) { |
| fs4.write(self.fd, buffer, offset, length, position, function(err, written, buffer2) { |
| cb(); |
| callback(err, written, buffer2); |
| }); |
| }); |
| }; |
| FdSlicer.prototype.createReadStream = function(options2) { |
| return new ReadStream(this, options2); |
| }; |
| FdSlicer.prototype.createWriteStream = function(options2) { |
| return new WriteStream(this, options2); |
| }; |
| FdSlicer.prototype.ref = function() { |
| this.refCount += 1; |
| }; |
| FdSlicer.prototype.unref = function() { |
| var self = this; |
| self.refCount -= 1; |
| if (self.refCount > 0) return; |
| if (self.refCount < 0) throw new Error("invalid unref"); |
| if (self.autoClose) { |
| fs4.close(self.fd, onCloseDone); |
| } |
| function onCloseDone(err) { |
| if (err) { |
| self.emit("error", err); |
| } else { |
| self.emit("close"); |
| } |
| } |
| }; |
| util2.inherits(ReadStream, Readable); |
| function ReadStream(context, options2) { |
| options2 = options2 || {}; |
| Readable.call(this, options2); |
| this.context = context; |
| this.context.ref(); |
| this.start = options2.start || 0; |
| this.endOffset = options2.end; |
| this.pos = this.start; |
| this._destroyed = false; |
| } |
| ReadStream.prototype._read = function(n) { |
| var self = this; |
| if (self._destroyed) return; |
| var toRead = Math.min(self._readableState.highWaterMark, n); |
| if (self.endOffset != null) { |
| toRead = Math.min(toRead, self.endOffset - self.pos); |
| } |
| if (toRead <= 0) { |
| self._destroyed = true; |
| self.push(null); |
| self.context.unref(); |
| return; |
| } |
| self.context.pend.go(function(cb) { |
| if (self._destroyed) return cb(); |
| var buffer = Buffer.allocUnsafe(toRead); |
| fs4.read(self.context.fd, buffer, 0, toRead, self.pos, function(err, bytesRead) { |
| if (self._destroyed) return cb(); |
| if (err) { |
| self.destroy(err); |
| } else if (bytesRead === 0) { |
| self._destroyed = true; |
| self.push(null); |
| self.context.unref(); |
| } else { |
| self.pos += bytesRead; |
| self.push(buffer.slice(0, bytesRead)); |
| } |
| cb(); |
| }); |
| }); |
| }; |
| ReadStream.prototype.destroy = function(err) { |
| if (err == null && !this.readableEnded) { |
| err = new Error("stream destroyed"); |
| } |
| return Readable.prototype.destroy.call(this, err); |
| }; |
| ReadStream.prototype._destroy = function(err, cb) { |
| if (!this._destroyed) { |
| this._destroyed = true; |
| this.context.unref(); |
| } |
| cb(err); |
| }; |
| util2.inherits(WriteStream, Writable); |
| function WriteStream(context, options2) { |
| options2 = options2 || {}; |
| Writable.call(this, options2); |
| this.context = context; |
| this.context.ref(); |
| this.start = options2.start || 0; |
| this.endOffset = options2.end == null ? Infinity : +options2.end; |
| this.bytesWritten = 0; |
| this.pos = this.start; |
| this._destroyed = false; |
| this.on("finish", this.destroy.bind(this)); |
| } |
| WriteStream.prototype._write = function(buffer, encoding, callback) { |
| var self = this; |
| if (self._destroyed) return; |
| if (self.pos + buffer.length > self.endOffset) { |
| var err = new Error("maximum file length exceeded"); |
| err.code = "ETOOBIG"; |
| self.destroy(); |
| callback(err); |
| return; |
| } |
| self.context.pend.go(function(cb) { |
| if (self._destroyed) return cb(); |
| fs4.write(self.context.fd, buffer, 0, buffer.length, self.pos, function(err2, bytes) { |
| if (err2) { |
| self.destroy(); |
| cb(); |
| callback(err2); |
| } else { |
| self.bytesWritten += bytes; |
| self.pos += bytes; |
| self.emit("progress"); |
| cb(); |
| callback(); |
| } |
| }); |
| }); |
| }; |
| WriteStream.prototype._destroy = function(err, cb) { |
| if (!this._destroyed) { |
| this._destroyed = true; |
| this.context.unref(); |
| } |
| cb(err); |
| }; |
| util2.inherits(BufferSlicer, EventEmitter); |
| function BufferSlicer(buffer, options2) { |
| EventEmitter.call(this); |
| options2 = options2 || {}; |
| this.refCount = 0; |
| this.buffer = buffer; |
| this.maxChunkSize = options2.maxChunkSize || Number.MAX_SAFE_INTEGER; |
| } |
| BufferSlicer.prototype.read = function(buffer, offset, length, position, callback) { |
| if (!(0 <= offset && offset <= buffer.length)) throw new RangeError("offset outside buffer: 0 <= " + offset + " <= " + buffer.length); |
| if (position < 0) throw new RangeError("position is negative: " + position); |
| if (offset + length > buffer.length) { |
| length = buffer.length - offset; |
| } |
| if (position + length > this.buffer.length) { |
| length = this.buffer.length - position; |
| } |
| if (length <= 0) { |
| setImmediate(function() { |
| callback(null, 0); |
| }); |
| return; |
| } |
| this.buffer.copy(buffer, offset, position, position + length); |
| setImmediate(function() { |
| callback(null, length); |
| }); |
| }; |
| BufferSlicer.prototype.write = function(buffer, offset, length, position, callback) { |
| buffer.copy(this.buffer, position, offset, offset + length); |
| setImmediate(function() { |
| callback(null, length, buffer); |
| }); |
| }; |
| BufferSlicer.prototype.createReadStream = function(options2) { |
| options2 = options2 || {}; |
| var readStream = new PassThrough(options2); |
| readStream._destroyed = false; |
| readStream.start = options2.start || 0; |
| readStream.endOffset = options2.end; |
| readStream.pos = readStream.endOffset || this.buffer.length; |
| var entireSlice = this.buffer.slice(readStream.start, readStream.pos); |
| var offset = 0; |
| while (true) { |
| var nextOffset = offset + this.maxChunkSize; |
| if (nextOffset >= entireSlice.length) { |
| if (offset < entireSlice.length) { |
| readStream.write(entireSlice.slice(offset, entireSlice.length)); |
| } |
| break; |
| } |
| readStream.write(entireSlice.slice(offset, nextOffset)); |
| offset = nextOffset; |
| } |
| readStream.end(); |
| readStream._destroy = function(err, cb) { |
| readStream._destroyed = true; |
| PassThrough.prototype._destroy.call(readStream, err, cb); |
| }; |
| return readStream; |
| }; |
| BufferSlicer.prototype.createWriteStream = function(options2) { |
| var bufferSlicer = this; |
| options2 = options2 || {}; |
| var writeStream = new Writable(options2); |
| writeStream.start = options2.start || 0; |
| writeStream.endOffset = options2.end == null ? this.buffer.length : +options2.end; |
| writeStream.bytesWritten = 0; |
| writeStream.pos = writeStream.start; |
| writeStream._destroyed = false; |
| writeStream._write = function(buffer, encoding, callback) { |
| if (writeStream._destroyed) return; |
| var end = writeStream.pos + buffer.length; |
| if (end > writeStream.endOffset) { |
| var err = new Error("maximum file length exceeded"); |
| err.code = "ETOOBIG"; |
| writeStream._destroyed = true; |
| callback(err); |
| return; |
| } |
| buffer.copy(bufferSlicer.buffer, writeStream.pos, 0, buffer.length); |
| writeStream.bytesWritten += buffer.length; |
| writeStream.pos = end; |
| writeStream.emit("progress"); |
| callback(); |
| }; |
| writeStream._destroy = function(err, cb) { |
| writeStream._destroyed = true; |
| cb(err); |
| }; |
| return writeStream; |
| }; |
| BufferSlicer.prototype.ref = function() { |
| this.refCount += 1; |
| }; |
| BufferSlicer.prototype.unref = function() { |
| this.refCount -= 1; |
| if (this.refCount < 0) { |
| throw new Error("invalid unref"); |
| } |
| }; |
| function createFromBuffer(buffer, options2) { |
| return new BufferSlicer(buffer, options2); |
| } |
| function createFromFd(fd, options2) { |
| return new FdSlicer(fd, options2); |
| } |
| } |
| }); |
|
|
| |
| var require_buffer_crc32 = __commonJS({ |
| "packages/utils/third_party/yauzl/buffer-crc32.js"(exports2, module2) { |
| "use strict"; |
| var Buffer2 = require("buffer").Buffer; |
| var CRC_TABLE = [ |
| 0, |
| 1996959894, |
| 3993919788, |
| 2567524794, |
| 124634137, |
| 1886057615, |
| 3915621685, |
| 2657392035, |
| 249268274, |
| 2044508324, |
| 3772115230, |
| 2547177864, |
| 162941995, |
| 2125561021, |
| 3887607047, |
| 2428444049, |
| 498536548, |
| 1789927666, |
| 4089016648, |
| 2227061214, |
| 450548861, |
| 1843258603, |
| 4107580753, |
| 2211677639, |
| 325883990, |
| 1684777152, |
| 4251122042, |
| 2321926636, |
| 335633487, |
| 1661365465, |
| 4195302755, |
| 2366115317, |
| 997073096, |
| 1281953886, |
| 3579855332, |
| 2724688242, |
| 1006888145, |
| 1258607687, |
| 3524101629, |
| 2768942443, |
| 901097722, |
| 1119000684, |
| 3686517206, |
| 2898065728, |
| 853044451, |
| 1172266101, |
| 3705015759, |
| 2882616665, |
| 651767980, |
| 1373503546, |
| 3369554304, |
| 3218104598, |
| 565507253, |
| 1454621731, |
| 3485111705, |
| 3099436303, |
| 671266974, |
| 1594198024, |
| 3322730930, |
| 2970347812, |
| 795835527, |
| 1483230225, |
| 3244367275, |
| 3060149565, |
| 1994146192, |
| 31158534, |
| 2563907772, |
| 4023717930, |
| 1907459465, |
| 112637215, |
| 2680153253, |
| 3904427059, |
| 2013776290, |
| 251722036, |
| 2517215374, |
| 3775830040, |
| 2137656763, |
| 141376813, |
| 2439277719, |
| 3865271297, |
| 1802195444, |
| 476864866, |
| 2238001368, |
| 4066508878, |
| 1812370925, |
| 453092731, |
| 2181625025, |
| 4111451223, |
| 1706088902, |
| 314042704, |
| 2344532202, |
| 4240017532, |
| 1658658271, |
| 366619977, |
| 2362670323, |
| 4224994405, |
| 1303535960, |
| 984961486, |
| 2747007092, |
| 3569037538, |
| 1256170817, |
| 1037604311, |
| 2765210733, |
| 3554079995, |
| 1131014506, |
| 879679996, |
| 2909243462, |
| 3663771856, |
| 1141124467, |
| 855842277, |
| 2852801631, |
| 3708648649, |
| 1342533948, |
| 654459306, |
| 3188396048, |
| 3373015174, |
| 1466479909, |
| 544179635, |
| 3110523913, |
| 3462522015, |
| 1591671054, |
| 702138776, |
| 2966460450, |
| 3352799412, |
| 1504918807, |
| 783551873, |
| 3082640443, |
| 3233442989, |
| 3988292384, |
| 2596254646, |
| 62317068, |
| 1957810842, |
| 3939845945, |
| 2647816111, |
| 81470997, |
| 1943803523, |
| 3814918930, |
| 2489596804, |
| 225274430, |
| 2053790376, |
| 3826175755, |
| 2466906013, |
| 167816743, |
| 2097651377, |
| 4027552580, |
| 2265490386, |
| 503444072, |
| 1762050814, |
| 4150417245, |
| 2154129355, |
| 426522225, |
| 1852507879, |
| 4275313526, |
| 2312317920, |
| 282753626, |
| 1742555852, |
| 4189708143, |
| 2394877945, |
| 397917763, |
| 1622183637, |
| 3604390888, |
| 2714866558, |
| 953729732, |
| 1340076626, |
| 3518719985, |
| 2797360999, |
| 1068828381, |
| 1219638859, |
| 3624741850, |
| 2936675148, |
| 906185462, |
| 1090812512, |
| 3747672003, |
| 2825379669, |
| 829329135, |
| 1181335161, |
| 3412177804, |
| 3160834842, |
| 628085408, |
| 1382605366, |
| 3423369109, |
| 3138078467, |
| 570562233, |
| 1426400815, |
| 3317316542, |
| 2998733608, |
| 733239954, |
| 1555261956, |
| 3268935591, |
| 3050360625, |
| 752459403, |
| 1541320221, |
| 2607071920, |
| 3965973030, |
| 1969922972, |
| 40735498, |
| 2617837225, |
| 3943577151, |
| 1913087877, |
| 83908371, |
| 2512341634, |
| 3803740692, |
| 2075208622, |
| 213261112, |
| 2463272603, |
| 3855990285, |
| 2094854071, |
| 198958881, |
| 2262029012, |
| 4057260610, |
| 1759359992, |
| 534414190, |
| 2176718541, |
| 4139329115, |
| 1873836001, |
| 414664567, |
| 2282248934, |
| 4279200368, |
| 1711684554, |
| 285281116, |
| 2405801727, |
| 4167216745, |
| 1634467795, |
| 376229701, |
| 2685067896, |
| 3608007406, |
| 1308918612, |
| 956543938, |
| 2808555105, |
| 3495958263, |
| 1231636301, |
| 1047427035, |
| 2932959818, |
| 3654703836, |
| 1088359270, |
| 936918e3, |
| 2847714899, |
| 3736837829, |
| 1202900863, |
| 817233897, |
| 3183342108, |
| 3401237130, |
| 1404277552, |
| 615818150, |
| 3134207493, |
| 3453421203, |
| 1423857449, |
| 601450431, |
| 3009837614, |
| 3294710456, |
| 1567103746, |
| 711928724, |
| 3020668471, |
| 3272380065, |
| 1510334235, |
| 755167117 |
| ]; |
| if (typeof Int32Array !== "undefined") { |
| CRC_TABLE = new Int32Array(CRC_TABLE); |
| } |
| function ensureBuffer(input) { |
| if (Buffer2.isBuffer(input)) { |
| return input; |
| } |
| var hasNewBufferAPI = typeof Buffer2.alloc === "function" && typeof Buffer2.from === "function"; |
| if (typeof input === "number") { |
| return hasNewBufferAPI ? Buffer2.alloc(input) : new Buffer2(input); |
| } else if (typeof input === "string") { |
| return hasNewBufferAPI ? Buffer2.from(input) : new Buffer2(input); |
| } else { |
| throw new Error("input must be buffer, number, or string, received " + typeof input); |
| } |
| } |
| function bufferizeInt(num) { |
| var tmp = ensureBuffer(4); |
| tmp.writeInt32BE(num, 0); |
| return tmp; |
| } |
| function _crc32(buf, previous) { |
| buf = ensureBuffer(buf); |
| if (Buffer2.isBuffer(previous)) { |
| previous = previous.readUInt32BE(0); |
| } |
| var crc = ~~previous ^ -1; |
| for (var n = 0; n < buf.length; n++) { |
| crc = CRC_TABLE[(crc ^ buf[n]) & 255] ^ crc >>> 8; |
| } |
| return crc ^ -1; |
| } |
| function crc32() { |
| return bufferizeInt(_crc32.apply(null, arguments)); |
| } |
| crc32.signed = function() { |
| return _crc32.apply(null, arguments); |
| }; |
| crc32.unsigned = function() { |
| return _crc32.apply(null, arguments) >>> 0; |
| }; |
| module2.exports = crc32; |
| } |
| }); |
|
|
| |
| var require_yauzl = __commonJS({ |
| "packages/utils/third_party/yauzl/index.js"(exports2) { |
| "use strict"; |
| var fs4 = require("fs"); |
| var zlib = require("zlib"); |
| var fd_slicer = require_fd_slicer(); |
| var crc32 = require_buffer_crc32(); |
| var util2 = require("util"); |
| var EventEmitter = require("events").EventEmitter; |
| var Transform = require("stream").Transform; |
| var PassThrough = require("stream").PassThrough; |
| var Writable = require("stream").Writable; |
| exports2.open = open2; |
| exports2.fromFd = fromFd; |
| exports2.fromBuffer = fromBuffer; |
| exports2.fromRandomAccessReader = fromRandomAccessReader; |
| exports2.dosDateTimeToDate = dosDateTimeToDate; |
| exports2.getFileNameLowLevel = getFileNameLowLevel; |
| exports2.validateFileName = validateFileName; |
| exports2.parseExtraFields = parseExtraFields; |
| exports2.ZipFile = ZipFile; |
| exports2.Entry = Entry; |
| exports2.LocalFileHeader = LocalFileHeader; |
| exports2.RandomAccessReader = RandomAccessReader; |
| function open2(path4, options2, callback) { |
| if (typeof options2 === "function") { |
| callback = options2; |
| options2 = null; |
| } |
| if (options2 == null) options2 = {}; |
| if (options2.autoClose == null) options2.autoClose = true; |
| if (options2.lazyEntries == null) options2.lazyEntries = false; |
| if (options2.decodeStrings == null) options2.decodeStrings = true; |
| if (options2.validateEntrySizes == null) options2.validateEntrySizes = true; |
| if (options2.strictFileNames == null) options2.strictFileNames = false; |
| if (callback == null) callback = defaultCallback; |
| fs4.open(path4, "r", function(err, fd) { |
| if (err) return callback(err); |
| fromFd(fd, options2, function(err2, zipfile) { |
| if (err2) fs4.close(fd, defaultCallback); |
| callback(err2, zipfile); |
| }); |
| }); |
| } |
| function fromFd(fd, options2, callback) { |
| if (typeof options2 === "function") { |
| callback = options2; |
| options2 = null; |
| } |
| if (options2 == null) options2 = {}; |
| if (options2.autoClose == null) options2.autoClose = false; |
| if (options2.lazyEntries == null) options2.lazyEntries = false; |
| if (options2.decodeStrings == null) options2.decodeStrings = true; |
| if (options2.validateEntrySizes == null) options2.validateEntrySizes = true; |
| if (options2.strictFileNames == null) options2.strictFileNames = false; |
| if (callback == null) callback = defaultCallback; |
| fs4.fstat(fd, function(err, stats) { |
| if (err) return callback(err); |
| var reader = fd_slicer.createFromFd(fd, { autoClose: true }); |
| fromRandomAccessReader(reader, stats.size, options2, callback); |
| }); |
| } |
| function fromBuffer(buffer, options2, callback) { |
| if (typeof options2 === "function") { |
| callback = options2; |
| options2 = null; |
| } |
| if (options2 == null) options2 = {}; |
| options2.autoClose = false; |
| if (options2.lazyEntries == null) options2.lazyEntries = false; |
| if (options2.decodeStrings == null) options2.decodeStrings = true; |
| if (options2.validateEntrySizes == null) options2.validateEntrySizes = true; |
| if (options2.strictFileNames == null) options2.strictFileNames = false; |
| var reader = fd_slicer.createFromBuffer(buffer, { maxChunkSize: 65536 }); |
| fromRandomAccessReader(reader, buffer.length, options2, callback); |
| } |
| function fromRandomAccessReader(reader, totalSize, options2, callback) { |
| if (typeof options2 === "function") { |
| callback = options2; |
| options2 = null; |
| } |
| if (options2 == null) options2 = {}; |
| if (options2.autoClose == null) options2.autoClose = true; |
| if (options2.lazyEntries == null) options2.lazyEntries = false; |
| if (options2.decodeStrings == null) options2.decodeStrings = true; |
| var decodeStrings = !!options2.decodeStrings; |
| if (options2.validateEntrySizes == null) options2.validateEntrySizes = true; |
| if (options2.strictFileNames == null) options2.strictFileNames = false; |
| if (callback == null) callback = defaultCallback; |
| if (typeof totalSize !== "number") throw new Error("expected totalSize parameter to be a number"); |
| if (totalSize > Number.MAX_SAFE_INTEGER) { |
| throw new Error("zip file too large. only file sizes up to 2^52 are supported due to JavaScript's Number type being an IEEE 754 double."); |
| } |
| reader.ref(); |
| var eocdrWithoutCommentSize = 22; |
| var zip64EocdlSize = 20; |
| var maxCommentSize = 65535; |
| var bufferSize = Math.min(zip64EocdlSize + eocdrWithoutCommentSize + maxCommentSize, totalSize); |
| var buffer = newBuffer(bufferSize); |
| var bufferReadStart = totalSize - buffer.length; |
| readAndAssertNoEof(reader, buffer, 0, bufferSize, bufferReadStart, function(err) { |
| if (err) return callback(err); |
| for (var i = bufferSize - eocdrWithoutCommentSize; i >= 0; i -= 1) { |
| if (buffer.readUInt32LE(i) !== 101010256) continue; |
| var eocdrBuffer = buffer.subarray(i); |
| var diskNumber = eocdrBuffer.readUInt16LE(4); |
| var entryCount = eocdrBuffer.readUInt16LE(10); |
| var centralDirectoryOffset = eocdrBuffer.readUInt32LE(16); |
| var commentLength = eocdrBuffer.readUInt16LE(20); |
| var expectedCommentLength = eocdrBuffer.length - eocdrWithoutCommentSize; |
| if (commentLength !== expectedCommentLength) { |
| return callback(new Error("Invalid comment length. Expected: " + expectedCommentLength + ". Found: " + commentLength + ". Are there extra bytes at the end of the file? Or is the end of central dir signature `PK\u263A\u263B` in the comment?")); |
| } |
| var comment = decodeStrings ? decodeBuffer(eocdrBuffer.subarray(22), false) : eocdrBuffer.subarray(22); |
| if (i - zip64EocdlSize >= 0 && buffer.readUInt32LE(i - zip64EocdlSize) === 117853008) { |
| var zip64EocdlBuffer = buffer.subarray(i - zip64EocdlSize, i - zip64EocdlSize + zip64EocdlSize); |
| var zip64EocdrOffset = readUInt64LE(zip64EocdlBuffer, 8); |
| var zip64EocdrBuffer = newBuffer(56); |
| return readAndAssertNoEof(reader, zip64EocdrBuffer, 0, zip64EocdrBuffer.length, zip64EocdrOffset, function(err2) { |
| if (err2) return callback(err2); |
| if (zip64EocdrBuffer.readUInt32LE(0) !== 101075792) { |
| return callback(new Error("invalid zip64 end of central directory record signature")); |
| } |
| diskNumber = zip64EocdrBuffer.readUInt32LE(16); |
| if (diskNumber !== 0) { |
| return callback(new Error("multi-disk zip files are not supported: found disk number: " + diskNumber)); |
| } |
| entryCount = readUInt64LE(zip64EocdrBuffer, 32); |
| centralDirectoryOffset = readUInt64LE(zip64EocdrBuffer, 48); |
| return callback(null, new ZipFile(reader, centralDirectoryOffset, totalSize, entryCount, comment, options2.autoClose, options2.lazyEntries, decodeStrings, options2.validateEntrySizes, options2.strictFileNames)); |
| }); |
| } |
| if (diskNumber !== 0) { |
| return callback(new Error("multi-disk zip files are not supported: found disk number: " + diskNumber)); |
| } |
| return callback(null, new ZipFile(reader, centralDirectoryOffset, totalSize, entryCount, comment, options2.autoClose, options2.lazyEntries, decodeStrings, options2.validateEntrySizes, options2.strictFileNames)); |
| } |
| callback(new Error("End of central directory record signature not found. Either not a zip file, or file is truncated.")); |
| }); |
| } |
| util2.inherits(ZipFile, EventEmitter); |
| function ZipFile(reader, centralDirectoryOffset, fileSize, entryCount, comment, autoClose, lazyEntries, decodeStrings, validateEntrySizes, strictFileNames) { |
| var self = this; |
| EventEmitter.call(self); |
| self.reader = reader; |
| self.reader.on("error", function(err) { |
| emitError(self, err); |
| }); |
| self.reader.once("close", function() { |
| self.emit("close"); |
| }); |
| self.readEntryCursor = centralDirectoryOffset; |
| self.fileSize = fileSize; |
| self.entryCount = entryCount; |
| self.comment = comment; |
| self.entriesRead = 0; |
| self.autoClose = !!autoClose; |
| self.lazyEntries = !!lazyEntries; |
| self.decodeStrings = !!decodeStrings; |
| self.validateEntrySizes = !!validateEntrySizes; |
| self.strictFileNames = !!strictFileNames; |
| self.isOpen = true; |
| self.emittedError = false; |
| if (!self.lazyEntries) self._readEntry(); |
| } |
| ZipFile.prototype.close = function() { |
| if (!this.isOpen) return; |
| this.isOpen = false; |
| this.reader.unref(); |
| }; |
| function emitErrorAndAutoClose(self, err) { |
| if (self.autoClose) self.close(); |
| emitError(self, err); |
| } |
| function emitError(self, err) { |
| if (self.emittedError) return; |
| self.emittedError = true; |
| self.emit("error", err); |
| } |
| ZipFile.prototype.readEntry = function() { |
| if (!this.lazyEntries) throw new Error("readEntry() called without lazyEntries:true"); |
| this._readEntry(); |
| }; |
| ZipFile.prototype._readEntry = function() { |
| var self = this; |
| if (self.entryCount === self.entriesRead) { |
| setImmediate(function() { |
| if (self.autoClose) self.close(); |
| if (self.emittedError) return; |
| self.emit("end"); |
| }); |
| return; |
| } |
| if (self.emittedError) return; |
| var buffer = newBuffer(46); |
| readAndAssertNoEof(self.reader, buffer, 0, buffer.length, self.readEntryCursor, function(err) { |
| if (err) return emitErrorAndAutoClose(self, err); |
| if (self.emittedError) return; |
| var entry = new Entry(); |
| var signature = buffer.readUInt32LE(0); |
| if (signature !== 33639248) return emitErrorAndAutoClose(self, new Error("invalid central directory file header signature: 0x" + signature.toString(16))); |
| entry.versionMadeBy = buffer.readUInt16LE(4); |
| entry.versionNeededToExtract = buffer.readUInt16LE(6); |
| entry.generalPurposeBitFlag = buffer.readUInt16LE(8); |
| entry.compressionMethod = buffer.readUInt16LE(10); |
| entry.lastModFileTime = buffer.readUInt16LE(12); |
| entry.lastModFileDate = buffer.readUInt16LE(14); |
| entry.crc32 = buffer.readUInt32LE(16); |
| entry.compressedSize = buffer.readUInt32LE(20); |
| entry.uncompressedSize = buffer.readUInt32LE(24); |
| entry.fileNameLength = buffer.readUInt16LE(28); |
| entry.extraFieldLength = buffer.readUInt16LE(30); |
| entry.fileCommentLength = buffer.readUInt16LE(32); |
| entry.internalFileAttributes = buffer.readUInt16LE(36); |
| entry.externalFileAttributes = buffer.readUInt32LE(38); |
| entry.relativeOffsetOfLocalHeader = buffer.readUInt32LE(42); |
| if (entry.generalPurposeBitFlag & 64) return emitErrorAndAutoClose(self, new Error("strong encryption is not supported")); |
| self.readEntryCursor += 46; |
| buffer = newBuffer(entry.fileNameLength + entry.extraFieldLength + entry.fileCommentLength); |
| readAndAssertNoEof(self.reader, buffer, 0, buffer.length, self.readEntryCursor, function(err2) { |
| if (err2) return emitErrorAndAutoClose(self, err2); |
| if (self.emittedError) return; |
| entry.fileNameRaw = buffer.subarray(0, entry.fileNameLength); |
| var fileCommentStart = entry.fileNameLength + entry.extraFieldLength; |
| entry.extraFieldRaw = buffer.subarray(entry.fileNameLength, fileCommentStart); |
| entry.fileCommentRaw = buffer.subarray(fileCommentStart, fileCommentStart + entry.fileCommentLength); |
| try { |
| entry.extraFields = parseExtraFields(entry.extraFieldRaw); |
| } catch (err3) { |
| return emitErrorAndAutoClose(self, err3); |
| } |
| if (self.decodeStrings) { |
| var isUtf8 = (entry.generalPurposeBitFlag & 2048) !== 0; |
| entry.fileComment = decodeBuffer(entry.fileCommentRaw, isUtf8); |
| entry.fileName = getFileNameLowLevel(entry.generalPurposeBitFlag, entry.fileNameRaw, entry.extraFields, self.strictFileNames); |
| var errorMessage = validateFileName(entry.fileName); |
| if (errorMessage != null) return emitErrorAndAutoClose(self, new Error(errorMessage)); |
| } else { |
| entry.fileComment = entry.fileCommentRaw; |
| entry.fileName = entry.fileNameRaw; |
| } |
| entry.comment = entry.fileComment; |
| self.readEntryCursor += buffer.length; |
| self.entriesRead += 1; |
| for (var i = 0; i < entry.extraFields.length; i++) { |
| var extraField = entry.extraFields[i]; |
| if (extraField.id !== 1) continue; |
| var zip64EiefBuffer = extraField.data; |
| var index = 0; |
| if (entry.uncompressedSize === 4294967295) { |
| if (index + 8 > zip64EiefBuffer.length) { |
| return emitErrorAndAutoClose(self, new Error("zip64 extended information extra field does not include uncompressed size")); |
| } |
| entry.uncompressedSize = readUInt64LE(zip64EiefBuffer, index); |
| index += 8; |
| } |
| if (entry.compressedSize === 4294967295) { |
| if (index + 8 > zip64EiefBuffer.length) { |
| return emitErrorAndAutoClose(self, new Error("zip64 extended information extra field does not include compressed size")); |
| } |
| entry.compressedSize = readUInt64LE(zip64EiefBuffer, index); |
| index += 8; |
| } |
| if (entry.relativeOffsetOfLocalHeader === 4294967295) { |
| if (index + 8 > zip64EiefBuffer.length) { |
| return emitErrorAndAutoClose(self, new Error("zip64 extended information extra field does not include relative header offset")); |
| } |
| entry.relativeOffsetOfLocalHeader = readUInt64LE(zip64EiefBuffer, index); |
| index += 8; |
| } |
| break; |
| } |
| if (self.validateEntrySizes && entry.compressionMethod === 0) { |
| var expectedCompressedSize = entry.uncompressedSize; |
| if (entry.isEncrypted()) { |
| expectedCompressedSize += 12; |
| } |
| if (entry.compressedSize !== expectedCompressedSize) { |
| var msg = "compressed/uncompressed size mismatch for stored file: " + entry.compressedSize + " != " + entry.uncompressedSize; |
| return emitErrorAndAutoClose(self, new Error(msg)); |
| } |
| } |
| self.emit("entry", entry); |
| if (!self.lazyEntries) self._readEntry(); |
| }); |
| }); |
| }; |
| ZipFile.prototype.openReadStream = function(entry, options2, callback) { |
| var self = this; |
| var relativeStart = 0; |
| var relativeEnd = entry.compressedSize; |
| if (callback == null) { |
| callback = options2; |
| options2 = null; |
| } |
| if (options2 == null) { |
| options2 = {}; |
| } else { |
| if (options2.decrypt != null) { |
| if (!entry.isEncrypted()) { |
| throw new Error("options.decrypt can only be specified for encrypted entries"); |
| } |
| if (options2.decrypt !== false) throw new Error("invalid options.decrypt value: " + options2.decrypt); |
| if (entry.isCompressed()) { |
| if (options2.decompress !== false) throw new Error("entry is encrypted and compressed, and options.decompress !== false"); |
| } |
| } |
| if (options2.decompress != null) { |
| if (!entry.isCompressed()) { |
| throw new Error("options.decompress can only be specified for compressed entries"); |
| } |
| if (!(options2.decompress === false || options2.decompress === true)) { |
| throw new Error("invalid options.decompress value: " + options2.decompress); |
| } |
| } |
| if (options2.start != null || options2.end != null) { |
| if (entry.isCompressed() && options2.decompress !== false) { |
| throw new Error("start/end range not allowed for compressed entry without options.decompress === false"); |
| } |
| if (entry.isEncrypted() && options2.decrypt !== false) { |
| throw new Error("start/end range not allowed for encrypted entry without options.decrypt === false"); |
| } |
| } |
| if (options2.start != null) { |
| relativeStart = options2.start; |
| if (relativeStart < 0) throw new Error("options.start < 0"); |
| if (relativeStart > entry.compressedSize) throw new Error("options.start > entry.compressedSize"); |
| } |
| if (options2.end != null) { |
| relativeEnd = options2.end; |
| if (relativeEnd < 0) throw new Error("options.end < 0"); |
| if (relativeEnd > entry.compressedSize) throw new Error("options.end > entry.compressedSize"); |
| if (relativeEnd < relativeStart) throw new Error("options.end < options.start"); |
| } |
| } |
| if (!self.isOpen) return callback(new Error("closed")); |
| if (entry.isEncrypted()) { |
| if (options2.decrypt !== false) return callback(new Error("entry is encrypted, and options.decrypt !== false")); |
| } |
| var decompress; |
| if (entry.compressionMethod === 0) { |
| decompress = false; |
| } else if (entry.compressionMethod === 8) { |
| decompress = options2.decompress != null ? options2.decompress : true; |
| } else { |
| return callback(new Error("unsupported compression method: " + entry.compressionMethod)); |
| } |
| self.readLocalFileHeader(entry, { minimal: true }, function(err, localFileHeader) { |
| if (err) return callback(err); |
| self.openReadStreamLowLevel( |
| localFileHeader.fileDataStart, |
| entry.compressedSize, |
| relativeStart, |
| relativeEnd, |
| decompress, |
| entry.uncompressedSize, |
| callback |
| ); |
| }); |
| }; |
| ZipFile.prototype.openReadStreamLowLevel = function(fileDataStart, compressedSize, relativeStart, relativeEnd, decompress, uncompressedSize, callback) { |
| var self = this; |
| var fileDataEnd = fileDataStart + compressedSize; |
| var readStream = self.reader.createReadStream({ |
| start: fileDataStart + relativeStart, |
| end: fileDataStart + relativeEnd |
| }); |
| var endpointStream = readStream; |
| if (decompress) { |
| var destroyed = false; |
| var inflateFilter = zlib.createInflateRaw(); |
| readStream.on("error", function(err) { |
| setImmediate(function() { |
| if (!destroyed) inflateFilter.emit("error", err); |
| }); |
| }); |
| readStream.pipe(inflateFilter); |
| if (self.validateEntrySizes) { |
| endpointStream = new AssertByteCountStream(uncompressedSize); |
| inflateFilter.on("error", function(err) { |
| setImmediate(function() { |
| if (!destroyed) endpointStream.emit("error", err); |
| }); |
| }); |
| inflateFilter.pipe(endpointStream); |
| } else { |
| endpointStream = inflateFilter; |
| } |
| installDestroyFn(endpointStream, function() { |
| destroyed = true; |
| if (inflateFilter !== endpointStream) inflateFilter.unpipe(endpointStream); |
| readStream.unpipe(inflateFilter); |
| readStream.destroy(); |
| }); |
| } |
| callback(null, endpointStream); |
| }; |
| ZipFile.prototype.readLocalFileHeader = function(entry, options2, callback) { |
| var self = this; |
| if (callback == null) { |
| callback = options2; |
| options2 = null; |
| } |
| if (options2 == null) options2 = {}; |
| self.reader.ref(); |
| var buffer = newBuffer(30); |
| readAndAssertNoEof(self.reader, buffer, 0, buffer.length, entry.relativeOffsetOfLocalHeader, function(err) { |
| try { |
| if (err) return callback(err); |
| var signature = buffer.readUInt32LE(0); |
| if (signature !== 67324752) { |
| return callback(new Error("invalid local file header signature: 0x" + signature.toString(16))); |
| } |
| var fileNameLength = buffer.readUInt16LE(26); |
| var extraFieldLength = buffer.readUInt16LE(28); |
| var fileDataStart = entry.relativeOffsetOfLocalHeader + 30 + fileNameLength + extraFieldLength; |
| if (fileDataStart + entry.compressedSize > self.fileSize) { |
| return callback(new Error("file data overflows file bounds: " + fileDataStart + " + " + entry.compressedSize + " > " + self.fileSize)); |
| } |
| if (options2.minimal) { |
| return callback(null, { fileDataStart }); |
| } |
| var localFileHeader = new LocalFileHeader(); |
| localFileHeader.fileDataStart = fileDataStart; |
| localFileHeader.versionNeededToExtract = buffer.readUInt16LE(4); |
| localFileHeader.generalPurposeBitFlag = buffer.readUInt16LE(6); |
| localFileHeader.compressionMethod = buffer.readUInt16LE(8); |
| localFileHeader.lastModFileTime = buffer.readUInt16LE(10); |
| localFileHeader.lastModFileDate = buffer.readUInt16LE(12); |
| localFileHeader.crc32 = buffer.readUInt32LE(14); |
| localFileHeader.compressedSize = buffer.readUInt32LE(18); |
| localFileHeader.uncompressedSize = buffer.readUInt32LE(22); |
| localFileHeader.fileNameLength = fileNameLength; |
| localFileHeader.extraFieldLength = extraFieldLength; |
| buffer = newBuffer(fileNameLength + extraFieldLength); |
| self.reader.ref(); |
| readAndAssertNoEof(self.reader, buffer, 0, buffer.length, entry.relativeOffsetOfLocalHeader + 30, function(err2) { |
| try { |
| if (err2) return callback(err2); |
| localFileHeader.fileName = buffer.subarray(0, fileNameLength); |
| localFileHeader.extraField = buffer.subarray(fileNameLength); |
| return callback(null, localFileHeader); |
| } finally { |
| self.reader.unref(); |
| } |
| }); |
| } finally { |
| self.reader.unref(); |
| } |
| }); |
| }; |
| function Entry() { |
| } |
| Entry.prototype.getLastModDate = function(options2) { |
| if (options2 == null) options2 = {}; |
| if (!options2.forceDosFormat) { |
| for (var i = 0; i < this.extraFields.length; i++) { |
| var extraField = this.extraFields[i]; |
| if (extraField.id === 21589) { |
| var data = extraField.data; |
| if (data.length < 5) continue; |
| var flags = data[0]; |
| var HAS_MTIME = 1; |
| if (!(flags & HAS_MTIME)) continue; |
| var posixTimestamp = data.readInt32LE(1); |
| return new Date(posixTimestamp * 1e3); |
| } else if (extraField.id === 10) { |
| var data = extraField.data; |
| if (data.length !== 32) continue; |
| if (data.readUInt16LE(4) !== 1) continue; |
| if (data.readUInt16LE(6) !== 24) continue; |
| var hundredNanoSecondsSince1601 = data.readUInt32LE(8) + 4294967296 * data.readInt32LE(12); |
| var millisecondsSince1970 = hundredNanoSecondsSince1601 / 1e4 - 116444736e5; |
| return new Date(millisecondsSince1970); |
| } |
| } |
| } |
| return dosDateTimeToDate(this.lastModFileDate, this.lastModFileTime, options2.timezone); |
| }; |
| Entry.prototype.isEncrypted = function() { |
| return (this.generalPurposeBitFlag & 1) !== 0; |
| }; |
| Entry.prototype.isCompressed = function() { |
| return this.compressionMethod === 8; |
| }; |
| function LocalFileHeader() { |
| } |
| function dosDateTimeToDate(date, time, timezone) { |
| var day = date & 31; |
| var month = (date >> 5 & 15) - 1; |
| var year = (date >> 9 & 127) + 1980; |
| var millisecond = 0; |
| var second = (time & 31) * 2; |
| var minute = time >> 5 & 63; |
| var hour = time >> 11 & 31; |
| if (timezone == null || timezone === "local") { |
| return new Date(year, month, day, hour, minute, second, millisecond); |
| } else if (timezone === "UTC") { |
| return new Date(Date.UTC(year, month, day, hour, minute, second, millisecond)); |
| } else { |
| throw new Error("unrecognized options.timezone: " + options.timezone); |
| } |
| } |
| function getFileNameLowLevel(generalPurposeBitFlag, fileNameBuffer, extraFields, strictFileNames) { |
| var fileName = null; |
| for (var i = 0; i < extraFields.length; i++) { |
| var extraField = extraFields[i]; |
| if (extraField.id === 28789) { |
| if (extraField.data.length < 6) { |
| continue; |
| } |
| if (extraField.data.readUInt8(0) !== 1) { |
| continue; |
| } |
| var oldNameCrc32 = extraField.data.readUInt32LE(1); |
| if (crc32.unsigned(fileNameBuffer) !== oldNameCrc32) { |
| continue; |
| } |
| fileName = decodeBuffer(extraField.data.subarray(5), true); |
| break; |
| } |
| } |
| if (fileName == null) { |
| var isUtf8 = (generalPurposeBitFlag & 2048) !== 0; |
| fileName = decodeBuffer(fileNameBuffer, isUtf8); |
| } |
| if (!strictFileNames) { |
| fileName = fileName.replace(/\\/g, "/"); |
| } |
| return fileName; |
| } |
| function validateFileName(fileName) { |
| if (fileName.indexOf("\\") !== -1) { |
| return "invalid characters in fileName: " + fileName; |
| } |
| if (/^[a-zA-Z]:/.test(fileName) || /^\//.test(fileName)) { |
| return "absolute path: " + fileName; |
| } |
| if (fileName.split("/").indexOf("..") !== -1) { |
| return "invalid relative path: " + fileName; |
| } |
| return null; |
| } |
| function parseExtraFields(extraFieldBuffer) { |
| var extraFields = []; |
| var i = 0; |
| while (i < extraFieldBuffer.length - 3) { |
| var headerId = extraFieldBuffer.readUInt16LE(i + 0); |
| var dataSize = extraFieldBuffer.readUInt16LE(i + 2); |
| var dataStart = i + 4; |
| var dataEnd = dataStart + dataSize; |
| if (dataEnd > extraFieldBuffer.length) throw new Error("extra field length exceeds extra field buffer size"); |
| var dataBuffer = extraFieldBuffer.subarray(dataStart, dataEnd); |
| extraFields.push({ |
| id: headerId, |
| data: dataBuffer |
| }); |
| i = dataEnd; |
| } |
| return extraFields; |
| } |
| function readAndAssertNoEof(reader, buffer, offset, length, position, callback) { |
| if (length === 0) { |
| return setImmediate(function() { |
| callback(null, newBuffer(0)); |
| }); |
| } |
| reader.read(buffer, offset, length, position, function(err, bytesRead) { |
| if (err) return callback(err); |
| if (bytesRead < length) { |
| return callback(new Error("unexpected EOF")); |
| } |
| callback(); |
| }); |
| } |
| util2.inherits(AssertByteCountStream, Transform); |
| function AssertByteCountStream(byteCount) { |
| Transform.call(this); |
| this.actualByteCount = 0; |
| this.expectedByteCount = byteCount; |
| } |
| AssertByteCountStream.prototype._transform = function(chunk, encoding, cb) { |
| this.actualByteCount += chunk.length; |
| if (this.actualByteCount > this.expectedByteCount) { |
| var msg = "too many bytes in the stream. expected " + this.expectedByteCount + ". got at least " + this.actualByteCount; |
| return cb(new Error(msg)); |
| } |
| cb(null, chunk); |
| }; |
| AssertByteCountStream.prototype._flush = function(cb) { |
| if (this.actualByteCount < this.expectedByteCount) { |
| var msg = "not enough bytes in the stream. expected " + this.expectedByteCount + ". got only " + this.actualByteCount; |
| return cb(new Error(msg)); |
| } |
| cb(); |
| }; |
| util2.inherits(RandomAccessReader, EventEmitter); |
| function RandomAccessReader() { |
| EventEmitter.call(this); |
| this.refCount = 0; |
| } |
| RandomAccessReader.prototype.ref = function() { |
| this.refCount += 1; |
| }; |
| RandomAccessReader.prototype.unref = function() { |
| var self = this; |
| self.refCount -= 1; |
| if (self.refCount > 0) return; |
| if (self.refCount < 0) throw new Error("invalid unref"); |
| self.close(onCloseDone); |
| function onCloseDone(err) { |
| if (err) return self.emit("error", err); |
| self.emit("close"); |
| } |
| }; |
| RandomAccessReader.prototype.createReadStream = function(options2) { |
| if (options2 == null) options2 = {}; |
| var start = options2.start; |
| var end = options2.end; |
| if (start === end) { |
| var emptyStream = new PassThrough(); |
| setImmediate(function() { |
| emptyStream.end(); |
| }); |
| return emptyStream; |
| } |
| var stream = this._readStreamForRange(start, end); |
| var destroyed = false; |
| var refUnrefFilter = new RefUnrefFilter(this); |
| stream.on("error", function(err) { |
| setImmediate(function() { |
| if (!destroyed) refUnrefFilter.emit("error", err); |
| }); |
| }); |
| installDestroyFn(refUnrefFilter, function() { |
| stream.unpipe(refUnrefFilter); |
| refUnrefFilter.unref(); |
| stream.destroy(); |
| }); |
| var byteCounter = new AssertByteCountStream(end - start); |
| refUnrefFilter.on("error", function(err) { |
| setImmediate(function() { |
| if (!destroyed) byteCounter.emit("error", err); |
| }); |
| }); |
| installDestroyFn(byteCounter, function() { |
| destroyed = true; |
| refUnrefFilter.unpipe(byteCounter); |
| refUnrefFilter.destroy(); |
| }); |
| return stream.pipe(refUnrefFilter).pipe(byteCounter); |
| }; |
| RandomAccessReader.prototype._readStreamForRange = function(start, end) { |
| throw new Error("not implemented"); |
| }; |
| RandomAccessReader.prototype.read = function(buffer, offset, length, position, callback) { |
| var readStream = this.createReadStream({ start: position, end: position + length }); |
| var writeStream = new Writable(); |
| var written = 0; |
| writeStream._write = function(chunk, encoding, cb) { |
| chunk.copy(buffer, offset + written, 0, chunk.length); |
| written += chunk.length; |
| cb(); |
| }; |
| writeStream.on("finish", callback); |
| readStream.on("error", function(error) { |
| callback(error); |
| }); |
| readStream.pipe(writeStream); |
| }; |
| RandomAccessReader.prototype.close = function(callback) { |
| setImmediate(callback); |
| }; |
| util2.inherits(RefUnrefFilter, PassThrough); |
| function RefUnrefFilter(context) { |
| PassThrough.call(this); |
| this.context = context; |
| this.context.ref(); |
| this.unreffedYet = false; |
| } |
| RefUnrefFilter.prototype._flush = function(cb) { |
| this.unref(); |
| cb(); |
| }; |
| RefUnrefFilter.prototype.unref = function(cb) { |
| if (this.unreffedYet) return; |
| this.unreffedYet = true; |
| this.context.unref(); |
| }; |
| var cp437 = "\0\u263A\u263B\u2665\u2666\u2663\u2660\u2022\u25D8\u25CB\u25D9\u2642\u2640\u266A\u266B\u263C\u25BA\u25C4\u2195\u203C\xB6\xA7\u25AC\u21A8\u2191\u2193\u2192\u2190\u221F\u2194\u25B2\u25BC !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u2302\xC7\xFC\xE9\xE2\xE4\xE0\xE5\xE7\xEA\xEB\xE8\xEF\xEE\xEC\xC4\xC5\xC9\xE6\xC6\xF4\xF6\xF2\xFB\xF9\xFF\xD6\xDC\xA2\xA3\xA5\u20A7\u0192\xE1\xED\xF3\xFA\xF1\xD1\xAA\xBA\xBF\u2310\xAC\xBD\xBC\xA1\xAB\xBB\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255D\u255C\u255B\u2510\u2514\u2534\u252C\u251C\u2500\u253C\u255E\u255F\u255A\u2554\u2569\u2566\u2560\u2550\u256C\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256B\u256A\u2518\u250C\u2588\u2584\u258C\u2590\u2580\u03B1\xDF\u0393\u03C0\u03A3\u03C3\xB5\u03C4\u03A6\u0398\u03A9\u03B4\u221E\u03C6\u03B5\u2229\u2261\xB1\u2265\u2264\u2320\u2321\xF7\u2248\xB0\u2219\xB7\u221A\u207F\xB2\u25A0\xA0"; |
| function decodeBuffer(buffer, isUtf8) { |
| if (isUtf8) { |
| return buffer.toString("utf8"); |
| } else { |
| var result = ""; |
| for (var i = 0; i < buffer.length; i++) { |
| result += cp437[buffer[i]]; |
| } |
| return result; |
| } |
| } |
| function readUInt64LE(buffer, offset) { |
| var lower32 = buffer.readUInt32LE(offset); |
| var upper32 = buffer.readUInt32LE(offset + 4); |
| return upper32 * 4294967296 + lower32; |
| } |
| var newBuffer; |
| if (typeof Buffer.allocUnsafe === "function") { |
| newBuffer = function(len) { |
| return Buffer.allocUnsafe(len); |
| }; |
| } else { |
| newBuffer = function(len) { |
| return new Buffer(len); |
| }; |
| } |
| function installDestroyFn(stream, fn) { |
| if (typeof stream.destroy === "function") { |
| stream._destroy = function(err, cb) { |
| fn(); |
| if (cb != null) cb(err); |
| }; |
| } else { |
| stream.destroy = fn; |
| } |
| } |
| function defaultCallback(err) { |
| if (err) throw err; |
| } |
| } |
| }); |
|
|
| |
| var import_common4 = require("../common"); |
|
|
| |
| var import_common3 = require("../common"); |
| var globals = __toESM(require("../globals")); |
| var import_expect = require("../matchers/expect"); |
|
|
| |
| var import_fs = __toESM(require("fs")); |
| var import_path = __toESM(require("path")); |
| var import_util = __toESM(require("util")); |
| var debug = require("playwright-core/lib/utilsBundle").debug; |
| var mime = require("playwright-core/lib/utilsBundle").mime; |
| var minimatch = require("playwright-core/lib/utilsBundle").minimatch; |
| var { calculateSha1 } = require("playwright-core/lib/coreBundle").utils; |
| var { sanitizeForFilePath } = require("playwright-core/lib/coreBundle").utils; |
| var { isRegExp } = require("playwright-core/lib/coreBundle").iso; |
| var { parseStackFrame, stringifyStackFrames } = require("playwright-core/lib/coreBundle").iso; |
| var { ansiRegex, isString, stripAnsiEscapes } = require("playwright-core/lib/coreBundle").iso; |
| var PLAYWRIGHT_TEST_PATH = import_path.default.join(__dirname, ".."); |
| var PLAYWRIGHT_CORE_PATH = import_path.default.dirname(require.resolve("playwright-core/package.json")); |
| function filterStackTrace(e) { |
| const name = e.name ? e.name + ": " : ""; |
| const cause = e.cause instanceof Error ? filterStackTrace(e.cause) : void 0; |
| if (process.env.PWDEBUGIMPL) |
| return { message: name + e.message, stack: e.stack || "", cause }; |
| const stackLines = stringifyStackFrames(filteredStackTrace(e.stack?.split("\n") || [])); |
| return { |
| message: name + e.message, |
| stack: `${name}${e.message}${stackLines.map((line) => "\n" + line).join("")}`, |
| cause |
| }; |
| } |
| function filterStackFile(file) { |
| if (process.env.PWDEBUGIMPL) |
| return true; |
| if (file.startsWith(PLAYWRIGHT_TEST_PATH)) |
| return false; |
| if (file.startsWith(PLAYWRIGHT_CORE_PATH)) |
| return false; |
| return true; |
| } |
| function filteredStackTrace(rawStack) { |
| const frames = []; |
| for (const line of rawStack) { |
| const frame = parseStackFrame(line, import_path.default.sep, !!process.env.PWDEBUGIMPL); |
| if (!frame || !frame.file) |
| continue; |
| if (!filterStackFile(frame.file)) |
| continue; |
| frames.push(frame); |
| } |
| return frames; |
| } |
| function serializeError(error) { |
| if (error instanceof Error) |
| return filterStackTrace(error); |
| return { |
| value: import_util.default.inspect(error) |
| }; |
| } |
| function relativeFilePath(file) { |
| if (!import_path.default.isAbsolute(file)) |
| return file; |
| return import_path.default.relative(process.cwd(), file); |
| } |
| function formatLocation(location) { |
| return relativeFilePath(location.file) + ":" + location.line + ":" + location.column; |
| } |
| var windowsFilesystemFriendlyLength = 60; |
| function trimLongString(s, length = 100) { |
| if (s.length <= length) |
| return s; |
| const hash = calculateSha1(s); |
| const middle = `-${hash.substring(0, 5)}-`; |
| const start = Math.floor((length - middle.length) / 2); |
| const end = length - middle.length - start; |
| return s.substring(0, start) + middle + s.slice(-end); |
| } |
| function addSuffixToFilePath(filePath, suffix) { |
| const ext = import_path.default.extname(filePath); |
| const base = filePath.substring(0, filePath.length - ext.length); |
| return base + suffix + ext; |
| } |
| function sanitizeFilePathBeforeExtension(filePath, ext) { |
| ext ??= import_path.default.extname(filePath); |
| const base = filePath.substring(0, filePath.length - ext.length); |
| return sanitizeForFilePath(base) + ext; |
| } |
| function getContainedPath(parentPath, subPath = "") { |
| const resolvedPath = import_path.default.resolve(parentPath, subPath); |
| if (resolvedPath === parentPath || resolvedPath.startsWith(parentPath + import_path.default.sep)) |
| return resolvedPath; |
| return null; |
| } |
| var debugTest = debug("pw:test"); |
| async function normalizeAndSaveAttachment(outputPath, name, options2 = {}) { |
| if (options2.path === void 0 && options2.body === void 0) |
| return { name, contentType: "text/plain" }; |
| if ((options2.path !== void 0 ? 1 : 0) + (options2.body !== void 0 ? 1 : 0) !== 1) |
| throw new Error(`Exactly one of "path" and "body" must be specified`); |
| if (options2.path !== void 0) { |
| const hash = calculateSha1(options2.path); |
| if (!isString(name)) |
| throw new Error('"name" should be string.'); |
| const sanitizedNamePrefix = sanitizeForFilePath(name) + "-"; |
| const dest = import_path.default.join(outputPath, "attachments", sanitizedNamePrefix + hash + import_path.default.extname(options2.path)); |
| await import_fs.default.promises.mkdir(import_path.default.dirname(dest), { recursive: true }); |
| await import_fs.default.promises.copyFile(options2.path, dest); |
| const contentType = options2.contentType ?? (mime.getType(import_path.default.basename(options2.path)) || "application/octet-stream"); |
| return { name, contentType, path: dest }; |
| } else { |
| const contentType = options2.contentType ?? (typeof options2.body === "string" ? "text/plain" : "application/octet-stream"); |
| return { name, contentType, body: typeof options2.body === "string" ? Buffer.from(options2.body) : options2.body }; |
| } |
| } |
|
|
| |
| var import_common = require("../common"); |
| var { ManualPromise } = require("playwright-core/lib/coreBundle").iso; |
| var { escapeWithQuotes } = require("playwright-core/lib/coreBundle").iso; |
| var Fixture = class { |
| constructor(runner, registration) { |
| this.failed = false; |
| this._deps = new Set(); |
| this._usages = new Set(); |
| this.runner = runner; |
| this.registration = registration; |
| this.value = null; |
| const isUserFixture = this.registration.location && filterStackFile(this.registration.location.file); |
| const title = this.registration.customTitle || this.registration.name; |
| const location = isUserFixture ? this.registration.location : void 0; |
| this._stepInfo = { title: `Fixture ${escapeWithQuotes(title, '"')}`, category: "fixture", location }; |
| if (this.registration.box === "self") |
| this._stepInfo = void 0; |
| else if (this.registration.box) |
| this._stepInfo.group = isUserFixture ? "configuration" : "internal"; |
| this._setupDescription = { |
| title, |
| phase: "setup", |
| location, |
| slot: this.registration.timeout !== void 0 ? { |
| timeout: this.registration.timeout, |
| elapsed: 0 |
| } : this.registration.scope === "worker" ? { |
| timeout: this.runner.workerFixtureTimeout, |
| elapsed: 0 |
| } : void 0 |
| }; |
| this._teardownDescription = { ...this._setupDescription, phase: "teardown" }; |
| } |
| async setup(testInfo, runnable) { |
| this.runner.instanceForId.set(this.registration.id, this); |
| if (typeof this.registration.fn !== "function") { |
| this.value = this.registration.fn; |
| return; |
| } |
| const run = () => testInfo._runWithTimeout({ ...runnable, fixture: this._setupDescription }, () => this._setupInternal(testInfo)); |
| if (this._stepInfo) |
| await testInfo._runAsStep(this._stepInfo, run); |
| else |
| await run(); |
| } |
| async _setupInternal(testInfo) { |
| const params = {}; |
| for (const name of this.registration.deps) { |
| const registration = this.runner.pool.resolve(name, this.registration); |
| const dep = this.runner.instanceForId.get(registration.id); |
| if (!dep) { |
| this.failed = true; |
| return; |
| } |
| dep._usages.add(this); |
| this._deps.add(dep); |
| params[name] = dep.value; |
| if (dep.failed) { |
| this.failed = true; |
| return; |
| } |
| } |
| let called = false; |
| const useFuncStarted = new ManualPromise(); |
| const useFunc = async (value) => { |
| if (called) |
| throw new Error(`Cannot provide fixture value for the second time`); |
| called = true; |
| this.value = value; |
| this._useFuncFinished = new ManualPromise(); |
| useFuncStarted.resolve(); |
| await this._useFuncFinished; |
| }; |
| const workerInfo = { config: testInfo.config, parallelIndex: testInfo.parallelIndex, workerIndex: testInfo.workerIndex, project: testInfo.project }; |
| const info = this.registration.scope === "worker" ? workerInfo : testInfo; |
| this._selfTeardownComplete = (async () => { |
| try { |
| await this.registration.fn(params, useFunc, info); |
| if (!useFuncStarted.isDone()) |
| throw new Error(`use() was not called in fixture "${this.registration.name}"`); |
| } catch (error) { |
| this.failed = true; |
| if (!useFuncStarted.isDone()) |
| useFuncStarted.reject(error); |
| else |
| throw error; |
| } |
| })(); |
| await useFuncStarted; |
| } |
| async teardown(testInfo, runnable) { |
| try { |
| const fixtureRunnable = { ...runnable, fixture: this._teardownDescription }; |
| if (!testInfo._timeoutManager.isTimeExhaustedFor(fixtureRunnable)) { |
| const run = () => testInfo._runWithTimeout(fixtureRunnable, () => this._teardownInternal()); |
| if (this._stepInfo) |
| await testInfo._runAsStep(this._stepInfo, run); |
| else |
| await run(); |
| } |
| } finally { |
| for (const dep of this._deps) |
| dep._usages.delete(this); |
| this.runner.instanceForId.delete(this.registration.id); |
| } |
| } |
| async _teardownInternal() { |
| if (typeof this.registration.fn !== "function") |
| return; |
| if (this._usages.size !== 0) { |
| console.error("Internal error: fixture integrity at", this._teardownDescription.title); |
| this._usages.clear(); |
| } |
| if (this._useFuncFinished) { |
| this._useFuncFinished.resolve(); |
| this._useFuncFinished = void 0; |
| await this._selfTeardownComplete; |
| } |
| } |
| _collectFixturesInTeardownOrder(scope, collector) { |
| if (this.registration.scope !== scope) |
| return; |
| for (const fixture of this._usages) |
| fixture._collectFixturesInTeardownOrder(scope, collector); |
| collector.add(this); |
| } |
| }; |
| var FixtureRunner = class { |
| constructor() { |
| this.testScopeClean = true; |
| this.instanceForId = new Map(); |
| this.workerFixtureTimeout = 0; |
| } |
| setPool(pool) { |
| if (!this.testScopeClean) |
| throw new Error("Did not teardown test scope"); |
| if (this.pool && pool.digest !== this.pool.digest) { |
| throw new Error([ |
| `Playwright detected inconsistent test.use() options.`, |
| `Most common mistakes that lead to this issue:`, |
| ` - Calling test.use() outside of the test file, for example in a common helper.`, |
| ` - One test file imports from another test file.` |
| ].join("\n")); |
| } |
| this.pool = pool; |
| } |
| _collectFixturesInSetupOrder(registration, collector) { |
| if (collector.has(registration)) |
| return; |
| for (const name of registration.deps) { |
| const dep = this.pool.resolve(name, registration); |
| this._collectFixturesInSetupOrder(dep, collector); |
| } |
| collector.add(registration); |
| } |
| async teardownScope(scope, testInfo, runnable) { |
| const allFixtures = Array.from(this.instanceForId.values()).reverse(); |
| const collector = new Set(); |
| for (const fixture of allFixtures) |
| fixture._collectFixturesInTeardownOrder(scope, collector); |
| let firstError; |
| for (const fixture of collector) { |
| try { |
| await fixture.teardown(testInfo, runnable); |
| } catch (error) { |
| firstError = firstError ?? error; |
| } |
| } |
| if (scope === "test") |
| this.testScopeClean = true; |
| if (firstError) |
| throw firstError; |
| } |
| async resolveParametersForFunction(fn, testInfo, autoFixtures, runnable) { |
| const collector = new Set(); |
| const auto = []; |
| for (const registration of this.pool.autoFixtures()) { |
| let shouldRun = true; |
| if (autoFixtures === "all-hooks-only") |
| shouldRun = registration.scope === "worker" || registration.auto === "all-hooks-included"; |
| else if (autoFixtures === "worker") |
| shouldRun = registration.scope === "worker"; |
| if (shouldRun) |
| auto.push(registration); |
| } |
| auto.sort((r1, r2) => (r1.scope === "worker" ? 0 : 1) - (r2.scope === "worker" ? 0 : 1)); |
| for (const registration of auto) |
| this._collectFixturesInSetupOrder(registration, collector); |
| const names = getRequiredFixtureNames(fn); |
| for (const name of names) |
| this._collectFixturesInSetupOrder(this.pool.resolve(name), collector); |
| for (const registration of collector) |
| await this._setupFixtureForRegistration(registration, testInfo, runnable); |
| const params = {}; |
| for (const name of names) { |
| const registration = this.pool.resolve(name); |
| const fixture = this.instanceForId.get(registration.id); |
| if (!fixture || fixture.failed) |
| return null; |
| params[name] = fixture.value; |
| } |
| return { result: params }; |
| } |
| async resolveParametersAndRunFunction(fn, testInfo, autoFixtures, runnable) { |
| const params = await this.resolveParametersForFunction(fn, testInfo, autoFixtures, runnable); |
| if (params === null) { |
| return null; |
| } |
| await testInfo._runWithTimeout(runnable, () => fn(params.result, testInfo)); |
| } |
| async _setupFixtureForRegistration(registration, testInfo, runnable) { |
| if (registration.scope === "test") |
| this.testScopeClean = false; |
| let fixture = this.instanceForId.get(registration.id); |
| if (fixture) |
| return fixture; |
| fixture = new Fixture(this, registration); |
| await fixture.setup(testInfo, runnable); |
| return fixture; |
| } |
| dependsOnWorkerFixturesOnly(fn, location) { |
| const names = getRequiredFixtureNames(fn, location); |
| for (const name of names) { |
| const registration = this.pool.resolve(name); |
| if (registration.scope !== "worker") |
| return false; |
| } |
| return true; |
| } |
| }; |
| function getRequiredFixtureNames(fn, location) { |
| return import_common.fixtures.fixtureParameterNames(fn, location ?? { file: "<unknown>", line: 1, column: 1 }, (e) => { |
| throw new Error(`${formatLocation(e.location)}: ${e.message}`); |
| }); |
| } |
|
|
| |
| var import_fs3 = __toESM(require("fs")); |
| var import_path3 = __toESM(require("path")); |
|
|
| |
| var colors = require("playwright-core/lib/utilsBundle").colors; |
| var { ManualPromise: ManualPromise2 } = require("playwright-core/lib/coreBundle").iso; |
| var { monotonicTime } = require("playwright-core/lib/coreBundle").iso; |
| var kMaxDeadline = 2147483647; |
| var TimeoutManager = class { |
| constructor(timeout) { |
| this._ignoreTimeouts = false; |
| this._slow = false; |
| this._defaultSlot = { timeout, elapsed: 0 }; |
| } |
| setIgnoreTimeouts(ignoreTimeouts) { |
| if (this._ignoreTimeouts === ignoreTimeouts) |
| return; |
| this._ignoreTimeouts = ignoreTimeouts; |
| if (this._running) { |
| if (ignoreTimeouts) |
| this._running.slot.elapsed += monotonicTime() - this._running.start; |
| else |
| this._running.start = monotonicTime(); |
| this._updateTimeout(this._running); |
| } |
| } |
| interrupt() { |
| if (this._running) |
| this._running.timeoutPromise.reject(this._createTimeoutError(this._running)); |
| } |
| isTimeExhaustedFor(runnable) { |
| const slot = runnable.fixture?.slot || runnable.slot || this._defaultSlot; |
| return slot.timeout > 0 && slot.elapsed >= slot.timeout - 1; |
| } |
| async withRunnable(runnable, cb) { |
| if (this._running) |
| throw new Error(`Internal error: duplicate runnable`); |
| const running = this._running = { |
| runnable, |
| slot: runnable.fixture?.slot || runnable.slot || this._defaultSlot, |
| start: monotonicTime(), |
| deadline: kMaxDeadline, |
| timer: void 0, |
| timeoutPromise: new ManualPromise2() |
| }; |
| let debugTitle = ""; |
| try { |
| if (debugTest.enabled) { |
| debugTitle = runnable.fixture ? `${runnable.fixture.phase} "${runnable.fixture.title}"` : runnable.type; |
| const location = runnable.location ? ` at "${formatLocation(runnable.location)}"` : ``; |
| debugTest(`started ${debugTitle}${location}`); |
| } |
| this._updateTimeout(running); |
| return await Promise.race([ |
| cb(), |
| running.timeoutPromise |
| ]); |
| } finally { |
| if (running.timer) |
| clearTimeout(running.timer); |
| running.timer = void 0; |
| running.slot.elapsed += monotonicTime() - running.start; |
| this._running = void 0; |
| if (debugTest.enabled) |
| debugTest(`finished ${debugTitle}`); |
| } |
| } |
| _updateTimeout(running) { |
| if (running.timer) |
| clearTimeout(running.timer); |
| running.timer = void 0; |
| if (this._ignoreTimeouts || !running.slot.timeout) { |
| running.deadline = kMaxDeadline; |
| return; |
| } |
| running.deadline = running.start + (running.slot.timeout - running.slot.elapsed); |
| const timeout = running.deadline - monotonicTime() + 1; |
| if (timeout <= 0) |
| running.timeoutPromise.reject(this._createTimeoutError(running)); |
| else |
| running.timer = setTimeout(() => running.timeoutPromise.reject(this._createTimeoutError(running)), timeout); |
| } |
| defaultSlot() { |
| return this._defaultSlot; |
| } |
| slow() { |
| if (this._slow) |
| return; |
| this._slow = true; |
| const slot = this._running ? this._running.slot : this._defaultSlot; |
| slot.timeout = slot.timeout * 3; |
| if (this._running) |
| this._updateTimeout(this._running); |
| } |
| setTimeout(timeout) { |
| const slot = this._running ? this._running.slot : this._defaultSlot; |
| slot.timeout = timeout; |
| if (this._running) |
| this._updateTimeout(this._running); |
| } |
| currentSlotDeadline() { |
| return this._running ? this._running.deadline : kMaxDeadline; |
| } |
| currentSlotType() { |
| return this._running ? this._running.runnable.type : "test"; |
| } |
| _createTimeoutError(running) { |
| let message = ""; |
| const timeout = running.slot.timeout; |
| const runnable = running.runnable; |
| switch (runnable.type) { |
| case "test": { |
| if (runnable.fixture) { |
| if (runnable.fixture.phase === "setup") |
| message = `Test timeout of ${timeout}ms exceeded while setting up "${runnable.fixture.title}".`; |
| else |
| message = `Tearing down "${runnable.fixture.title}" exceeded the test timeout of ${timeout}ms.`; |
| } else { |
| message = `Test timeout of ${timeout}ms exceeded.`; |
| } |
| break; |
| } |
| case "afterEach": |
| case "beforeEach": |
| message = `Test timeout of ${timeout}ms exceeded while running "${runnable.type}" hook.`; |
| break; |
| case "beforeAll": |
| case "afterAll": |
| message = `"${runnable.type}" hook timeout of ${timeout}ms exceeded.`; |
| break; |
| case "teardown": { |
| if (runnable.fixture) |
| message = `Worker teardown timeout of ${timeout}ms exceeded while ${runnable.fixture.phase === "setup" ? "setting up" : "tearing down"} "${runnable.fixture.title}".`; |
| else |
| message = `Worker teardown timeout of ${timeout}ms exceeded.`; |
| break; |
| } |
| case "skip": |
| case "slow": |
| case "fixme": |
| case "fail": |
| message = `"${runnable.type}" modifier timeout of ${timeout}ms exceeded.`; |
| break; |
| } |
| const fixtureWithSlot = runnable.fixture?.slot ? runnable.fixture : void 0; |
| if (fixtureWithSlot) |
| message = `Fixture "${fixtureWithSlot.title}" timeout of ${timeout}ms exceeded during ${fixtureWithSlot.phase}.`; |
| message = colors.red(message); |
| const location = (fixtureWithSlot || runnable).location; |
| const error = new TimeoutManagerError(message); |
| error.name = ""; |
| error.stack = message + (location ? ` |
| at ${location.file}:${location.line}:${location.column}` : ""); |
| return error; |
| } |
| }; |
| var TimeoutManagerError = class extends Error { |
| }; |
|
|
| |
| var import_fs2 = __toESM(require("fs")); |
| var import_path2 = __toESM(require("path")); |
| var yauzl = __toESM(require_yauzl()); |
| var import_coreBundle = require("playwright-core/lib/coreBundle"); |
| var yazl = require("playwright-core/lib/utilsBundle").yazl; |
| var { ManualPromise: ManualPromise3 } = require("playwright-core/lib/coreBundle").iso; |
| var { monotonicTime: monotonicTime2 } = require("playwright-core/lib/coreBundle").iso; |
| var { calculateSha1: calculateSha12, createGuid } = require("playwright-core/lib/coreBundle").utils; |
| var { SerializedFS } = require("playwright-core/lib/coreBundle").utils; |
| var testTraceEntryName = "test.trace"; |
| var version = 8; |
| var traceOrdinal = 0; |
| var TestTracing = class { |
| constructor(testInfo, artifactsDir) { |
| this._traceEvents = []; |
| this._temporaryTraceFiles = []; |
| this._didFinishTestFunctionAndAfterEachHooks = false; |
| this._testInfo = testInfo; |
| this._artifactsDir = artifactsDir; |
| this._tracesDir = import_path2.default.join(this._artifactsDir, "traces"); |
| this._contextCreatedEvent = { |
| version, |
| type: "context-options", |
| origin: "testRunner", |
| browserName: "", |
| playwrightVersion: (0, import_coreBundle.getPlaywrightVersion)(), |
| options: {}, |
| platform: process.platform, |
| wallTime: Date.now(), |
| monotonicTime: monotonicTime2(), |
| sdkLanguage: "javascript" |
| }; |
| this._appendTraceEvent(this._contextCreatedEvent); |
| } |
| _shouldCaptureTrace() { |
| if (this._options?.mode === "on") |
| return true; |
| if (this._options?.mode === "retain-on-failure") |
| return true; |
| if (this._options?.mode === "on-first-retry" && this._testInfo.retry === 1) |
| return true; |
| if (this._options?.mode === "on-all-retries" && this._testInfo.retry > 0) |
| return true; |
| if (this._options?.mode === "retain-on-first-failure" && this._testInfo.retry === 0) |
| return true; |
| if (this._options?.mode === "retain-on-failure-and-retries") |
| return true; |
| return false; |
| } |
| async startIfNeeded(value) { |
| const defaultTraceOptions = { screenshots: true, snapshots: true, sources: true, attachments: true, live: false, mode: "off" }; |
| if (!value) { |
| this._options = defaultTraceOptions; |
| } else if (typeof value === "string") { |
| this._options = { ...defaultTraceOptions, mode: value === "retry-with-trace" ? "on-first-retry" : value }; |
| } else { |
| const mode = value.mode || "off"; |
| this._options = { ...defaultTraceOptions, ...value, mode: mode === "retry-with-trace" ? "on-first-retry" : mode }; |
| } |
| if (!this._shouldCaptureTrace()) { |
| this._options = void 0; |
| return; |
| } |
| if (!this._liveTraceFile && this._options.live) { |
| this._liveTraceFile = { file: import_path2.default.join(this._tracesDir, `${this._testInfo.testId}-test.trace`), fs: new SerializedFS() }; |
| this._liveTraceFile.fs.mkdir(import_path2.default.dirname(this._liveTraceFile.file)); |
| const data = this._traceEvents.map((e) => JSON.stringify(e)).join("\n") + "\n"; |
| this._liveTraceFile.fs.writeFile(this._liveTraceFile.file, data); |
| } |
| } |
| didFinishTestFunctionAndAfterEachHooks() { |
| this._didFinishTestFunctionAndAfterEachHooks = true; |
| } |
| artifactsDir() { |
| return this._artifactsDir; |
| } |
| tracesDir() { |
| return this._tracesDir; |
| } |
| traceTitle() { |
| return [import_path2.default.relative(this._testInfo.project.testDir, this._testInfo.file) + ":" + this._testInfo.line, ...this._testInfo.titlePath.slice(1)].join(" \u203A "); |
| } |
| generateNextTraceRecordingName() { |
| const ordinalSuffix = traceOrdinal ? `-recording${traceOrdinal}` : ""; |
| ++traceOrdinal; |
| const retrySuffix = this._testInfo.retry ? `-retry${this._testInfo.retry}` : ""; |
| return `${this._testInfo.testId}${retrySuffix}${ordinalSuffix}`; |
| } |
| _generateNextTraceRecordingPath() { |
| const file = import_path2.default.join(this._artifactsDir, createGuid() + ".zip"); |
| this._temporaryTraceFiles.push(file); |
| return file; |
| } |
| traceOptions() { |
| return this._options; |
| } |
| maybeGenerateNextTraceRecordingPath() { |
| if (this._didFinishTestFunctionAndAfterEachHooks && this._shouldAbandonTrace()) |
| return; |
| return this._generateNextTraceRecordingPath(); |
| } |
| _shouldAbandonTrace() { |
| if (!this._options) |
| return true; |
| const testFailed = this._testInfo.status !== this._testInfo.expectedStatus; |
| if (this._options.mode === "retain-on-failure-and-retries") |
| return !testFailed && this._testInfo.retry === 0; |
| return !testFailed && (this._options.mode === "retain-on-failure" || this._options.mode === "retain-on-first-failure"); |
| } |
| async stopIfNeeded() { |
| this._contextCreatedEvent.testTimeout = this._testInfo.timeout; |
| if (!this._options) |
| return; |
| const error = await this._liveTraceFile?.fs.syncAndGetError(); |
| if (error) |
| throw error; |
| if (this._shouldAbandonTrace()) { |
| for (const file of this._temporaryTraceFiles) |
| await import_fs2.default.promises.unlink(file).catch(() => { |
| }); |
| return; |
| } |
| const zipFile = new yazl.ZipFile(); |
| if (!this._options?.attachments) { |
| for (const event of this._traceEvents) { |
| if (event.type === "after") |
| delete event.attachments; |
| } |
| } |
| if (this._options?.sources) { |
| const sourceFiles = new Set(); |
| for (const event of this._traceEvents) { |
| if (event.type === "before") { |
| for (const frame of event.stack || []) |
| sourceFiles.add(frame.file); |
| } |
| } |
| for (const sourceFile of sourceFiles) { |
| await import_fs2.default.promises.readFile(sourceFile, "utf8").then((source) => { |
| zipFile.addBuffer(Buffer.from(source), "resources/src@" + calculateSha12(sourceFile) + ".txt"); |
| }).catch(() => { |
| }); |
| } |
| } |
| const sha1s = new Set(); |
| for (const event of this._traceEvents.filter((e) => e.type === "after")) { |
| for (const attachment of event.attachments || []) { |
| let contentPromise; |
| if (attachment.path) |
| contentPromise = import_fs2.default.promises.readFile(attachment.path).catch(() => void 0); |
| else if (attachment.base64) |
| contentPromise = Promise.resolve(Buffer.from(attachment.base64, "base64")); |
| const content = await contentPromise; |
| if (content === void 0) |
| continue; |
| const sha1 = calculateSha12(content); |
| attachment.sha1 = sha1; |
| delete attachment.path; |
| delete attachment.base64; |
| if (sha1s.has(sha1)) |
| continue; |
| sha1s.add(sha1); |
| zipFile.addBuffer(content, "resources/" + sha1); |
| } |
| } |
| const traceContent = Buffer.from(this._traceEvents.map((e) => JSON.stringify(e)).join("\n")); |
| zipFile.addBuffer(traceContent, testTraceEntryName); |
| await new Promise((f) => { |
| zipFile.end(void 0, () => { |
| zipFile.outputStream.pipe(import_fs2.default.createWriteStream(this._generateNextTraceRecordingPath())).on("close", f); |
| }); |
| }); |
| const tracePath = this._testInfo.outputPath("trace.zip"); |
| await mergeTraceFiles(tracePath, this._temporaryTraceFiles); |
| this._testInfo.attachments.push({ name: "trace", path: tracePath, contentType: "application/zip" }); |
| } |
| appendForError(error) { |
| const rawStack = error.stack?.split("\n") || []; |
| const stack = rawStack ? filteredStackTrace(rawStack) : []; |
| this._appendTraceEvent({ |
| type: "error", |
| message: this._formatError(error), |
| stack |
| }); |
| } |
| _formatError(error) { |
| const parts = [error.message || String(error.value)]; |
| if (error.cause) |
| parts.push("[cause]: " + this._formatError(error.cause)); |
| return parts.join("\n"); |
| } |
| appendStdioToTrace(type, chunk) { |
| this._appendTraceEvent({ |
| type, |
| timestamp: monotonicTime2(), |
| text: typeof chunk === "string" ? chunk : void 0, |
| base64: typeof chunk === "string" ? void 0 : chunk.toString("base64") |
| }); |
| } |
| appendBeforeActionForStep(options2) { |
| this._appendTraceEvent({ |
| type: "before", |
| callId: options2.stepId, |
| stepId: options2.stepId, |
| parentId: options2.parentId, |
| startTime: monotonicTime2(), |
| class: "Test", |
| method: options2.category, |
| title: options2.title, |
| params: Object.fromEntries(Object.entries(options2.params || {}).map(([name, value]) => [name, generatePreview(value)])), |
| stack: options2.stack, |
| group: options2.group |
| }); |
| } |
| appendAfterActionForStep(callId, error, attachments = [], annotations) { |
| this._appendTraceEvent({ |
| type: "after", |
| callId, |
| endTime: monotonicTime2(), |
| attachments: serializeAttachments(attachments), |
| annotations, |
| error |
| }); |
| } |
| _appendTraceEvent(event) { |
| this._traceEvents.push(event); |
| if (this._liveTraceFile) |
| this._liveTraceFile.fs.appendFile(this._liveTraceFile.file, JSON.stringify(event) + "\n", true); |
| } |
| }; |
| function serializeAttachments(attachments) { |
| if (attachments.length === 0) |
| return void 0; |
| return attachments.filter((a) => a.name !== "trace").map((a) => { |
| return { |
| name: a.name, |
| contentType: a.contentType, |
| path: a.path, |
| base64: a.body?.toString("base64") |
| }; |
| }); |
| } |
| function generatePreview(value, visited = new Set()) { |
| if (visited.has(value)) |
| return ""; |
| visited.add(value); |
| if (typeof value === "string") |
| return value; |
| if (typeof value === "number") |
| return value.toString(); |
| if (typeof value === "boolean") |
| return value.toString(); |
| if (value === null) |
| return "null"; |
| if (value === void 0) |
| return "undefined"; |
| if (Array.isArray(value)) |
| return "[" + value.map((v) => generatePreview(v, visited)).join(", ") + "]"; |
| if (typeof value === "object") |
| return "Object"; |
| return String(value); |
| } |
| async function mergeTraceFiles(fileName, temporaryTraceFiles) { |
| temporaryTraceFiles = temporaryTraceFiles.filter((file) => import_fs2.default.existsSync(file)); |
| if (temporaryTraceFiles.length === 1) { |
| await import_fs2.default.promises.rename(temporaryTraceFiles[0], fileName); |
| return; |
| } |
| const mergePromise = new ManualPromise3(); |
| const zipFile = new yazl.ZipFile(); |
| const entryNames = new Set(); |
| zipFile.on("error", (error) => mergePromise.reject(error)); |
| for (let i = temporaryTraceFiles.length - 1; i >= 0; --i) { |
| const tempFile = temporaryTraceFiles[i]; |
| const promise = new ManualPromise3(); |
| yauzl.open(tempFile, (err, inZipFile) => { |
| if (err) { |
| promise.reject(err); |
| return; |
| } |
| let pendingEntries = inZipFile.entryCount; |
| inZipFile.on("entry", (entry) => { |
| let entryName = entry.fileName; |
| if (entry.fileName === testTraceEntryName) { |
| } else if (entry.fileName.match(/trace\.[a-z]*$/)) { |
| entryName = i + "-" + entry.fileName; |
| } |
| if (entryNames.has(entryName)) { |
| if (--pendingEntries === 0) |
| promise.resolve(); |
| return; |
| } |
| entryNames.add(entryName); |
| inZipFile.openReadStream(entry, (err2, readStream) => { |
| if (err2) { |
| promise.reject(err2); |
| return; |
| } |
| zipFile.addReadStream(readStream, entryName); |
| if (--pendingEntries === 0) |
| promise.resolve(); |
| }); |
| }); |
| }); |
| await promise; |
| } |
| zipFile.end(void 0, () => { |
| zipFile.outputStream.pipe(import_fs2.default.createWriteStream(fileName)).on("close", () => { |
| void Promise.all(temporaryTraceFiles.map((tempFile) => import_fs2.default.promises.unlink(tempFile))).then(() => { |
| mergePromise.resolve(); |
| }).catch((error) => mergePromise.reject(error)); |
| }).on("error", (error) => mergePromise.reject(error)); |
| }); |
| await mergePromise; |
| } |
|
|
| |
| function testInfoError(error) { |
| const result = serializeError(error); |
| const matcherResult = error instanceof Error ? error.matcherResult : void 0; |
| if (matcherResult?.ariaSnapshot !== void 0) |
| result.errorContext = matcherResult.ariaSnapshot; |
| return result; |
| } |
|
|
| |
| var import_common2 = require("../common"); |
| var { ManualPromise: ManualPromise4 } = require("playwright-core/lib/coreBundle").iso; |
| var { captureRawStack, stringifyStackFrames: stringifyStackFrames2 } = require("playwright-core/lib/coreBundle").iso; |
| var { escapeWithQuotes: escapeWithQuotes2 } = require("playwright-core/lib/coreBundle").iso; |
| var { monotonicTime: monotonicTime3 } = require("playwright-core/lib/coreBundle").iso; |
| var { createGuid: createGuid2 } = require("playwright-core/lib/coreBundle").utils; |
| var { sanitizeForFilePath: sanitizeForFilePath2 } = require("playwright-core/lib/coreBundle").utils; |
| var { currentZone } = require("playwright-core/lib/coreBundle").utils; |
| var emtpyTestInfoCallbacks = { |
| onStepBegin: () => { |
| }, |
| onStepEnd: () => { |
| }, |
| onAttach: () => { |
| }, |
| onTestPaused: () => Promise.reject(new Error("TestInfoImpl not initialized")) |
| }; |
| var TestInfoImpl = class { |
| constructor(configInternal, projectInternal, workerParams, test, retry, callbacks) { |
| this._snapshotNames = { lastAnonymousSnapshotIndex: 0, lastNamedSnapshotIndex: {} }; |
| this._ariaSnapshotNames = { lastAnonymousSnapshotIndex: 0, lastNamedSnapshotIndex: {} }; |
| this._interruptedPromise = new ManualPromise4(); |
| this._lastStepId = 0; |
| this._steps = []; |
| this._stepMap = new Map(); |
| this._onDidFinishTestFunctionCallbacks = new Set(); |
| this._hasNonRetriableError = false; |
| this._hasUnhandledError = false; |
| this._allowSkips = false; |
| this.duration = 0; |
| this.annotations = []; |
| this.attachments = []; |
| this.status = "passed"; |
| this.snapshotSuffix = ""; |
| this.errors = []; |
| this._ignoreTimeoutsCounter = 0; |
| this.testId = test?.id ?? ""; |
| this._callbacks = callbacks; |
| this._startTime = monotonicTime3(); |
| this._startWallTime = Date.now(); |
| this._requireFile = test?._requireFile ?? ""; |
| this._uniqueSymbol = Symbol("testInfoUniqueSymbol"); |
| this._workerParams = workerParams; |
| this.repeatEachIndex = workerParams.repeatEachIndex; |
| this.retry = retry; |
| this.workerIndex = workerParams.workerIndex; |
| this.parallelIndex = workerParams.parallelIndex; |
| this._projectInternal = projectInternal; |
| this.project = projectInternal.project; |
| this._configInternal = configInternal; |
| this.config = configInternal.config; |
| this.title = test?.title ?? ""; |
| this.titlePath = test?.titlePath() ?? []; |
| this.file = test?.location.file ?? ""; |
| this.line = test?.location.line ?? 0; |
| this.column = test?.location.column ?? 0; |
| this.tags = test?.tags ?? []; |
| this.fn = test?.fn ?? (() => { |
| }); |
| this.expectedStatus = test?.expectedStatus ?? "skipped"; |
| this._timeoutManager = new TimeoutManager(this.project.timeout); |
| if (configInternal.configCLIOverrides.debug === "inspector") |
| this._setIgnoreTimeouts(true); |
| this.outputDir = (() => { |
| const relativeTestFilePath = import_path3.default.relative(this.project.testDir, this._requireFile.replace(/\.(spec|test)\.(js|ts|jsx|tsx|mjs|mts|cjs|cts)$/, "")); |
| const sanitizedRelativePath = relativeTestFilePath.replace(process.platform === "win32" ? new RegExp("\\\\", "g") : new RegExp("/", "g"), "-"); |
| const fullTitleWithoutSpec = this.titlePath.slice(1).join(" "); |
| let testOutputDir = trimLongString(sanitizedRelativePath + "-" + sanitizeForFilePath2(fullTitleWithoutSpec), windowsFilesystemFriendlyLength); |
| if (projectInternal.id) |
| testOutputDir += "-" + sanitizeForFilePath2(projectInternal.id); |
| if (this.retry) |
| testOutputDir += "-retry" + this.retry; |
| if (this.repeatEachIndex) |
| testOutputDir += "-repeat" + this.repeatEachIndex; |
| return import_path3.default.join(this.project.outputDir, testOutputDir); |
| })(); |
| this.snapshotDir = (() => { |
| const relativeTestFilePath = import_path3.default.relative(this.project.testDir, this._requireFile); |
| return import_path3.default.join(this.project.snapshotDir, relativeTestFilePath + "-snapshots"); |
| })(); |
| this._attachmentsPush = this.attachments.push.bind(this.attachments); |
| const attachmentsPush = (...attachments) => { |
| for (const a of attachments) |
| this._attach(a, this._parentStep()?.stepId); |
| return this.attachments.length; |
| }; |
| Object.defineProperty(this.attachments, "push", { |
| value: attachmentsPush, |
| writable: true, |
| enumerable: false, |
| configurable: true |
| }); |
| this._tracing = new TestTracing(this, workerParams.artifactsDir); |
| this.skip = import_common2.transform.wrapFunctionWithLocation((location, ...args) => this._modifier("skip", location, args)); |
| this.fixme = import_common2.transform.wrapFunctionWithLocation((location, ...args) => this._modifier("fixme", location, args)); |
| this.fail = import_common2.transform.wrapFunctionWithLocation((location, ...args) => this._modifier("fail", location, args)); |
| this.slow = import_common2.transform.wrapFunctionWithLocation((location, ...args) => this._modifier("slow", location, args)); |
| } |
| get error() { |
| return this.errors[0]; |
| } |
| set error(e) { |
| if (e === void 0) |
| throw new Error("Cannot assign testInfo.error undefined value!"); |
| this.errors[0] = e; |
| } |
| get timeout() { |
| return this._timeoutManager.defaultSlot().timeout; |
| } |
| set timeout(timeout) { |
| } |
| _deadline() { |
| return { deadline: this._timeoutManager.currentSlotDeadline(), timeout: this.timeout }; |
| } |
| _modifier(type, location, modifierArgs) { |
| if (typeof modifierArgs[1] === "function") { |
| throw new Error([ |
| "It looks like you are calling test.skip() inside the test and pass a callback.", |
| "Pass a condition instead and optional description instead:", |
| `test('my test', async ({ page, isMobile }) => {`, |
| ` test.skip(isMobile, 'This test is not applicable on mobile');`, |
| `});` |
| ].join("\n")); |
| } |
| if (modifierArgs.length >= 1 && !modifierArgs[0]) |
| return; |
| const description = modifierArgs[1]; |
| this.annotations.push({ type, description, location }); |
| if (type === "slow") { |
| this._timeoutManager.slow(); |
| } else if (type === "skip" || type === "fixme") { |
| this.expectedStatus = "skipped"; |
| throw new TestSkipError("Test is skipped: " + (description || "")); |
| } else if (type === "fail") { |
| if (this.expectedStatus !== "skipped") |
| this.expectedStatus = "failed"; |
| } |
| } |
| _findLastPredefinedStep(steps) { |
| for (let i = steps.length - 1; i >= 0; i--) { |
| const child = this._findLastPredefinedStep(steps[i].steps); |
| if (child) |
| return child; |
| if ((steps[i].category === "hook" || steps[i].category === "fixture") && !steps[i].endWallTime) |
| return steps[i]; |
| } |
| } |
| _parentStep() { |
| return currentZone().data("stepZone") ?? this._findLastPredefinedStep(this._steps); |
| } |
| _addStep(data, parentStep) { |
| const stepId = `${data.category}@${++this._lastStepId}`; |
| if (data.category === "hook" || data.category === "fixture") { |
| parentStep = this._findLastPredefinedStep(this._steps); |
| } else { |
| if (!parentStep) |
| parentStep = this._parentStep(); |
| } |
| const filteredStack = filteredStackTrace(captureRawStack()); |
| let boxedStack = parentStep?.boxedStack; |
| let location = data.location; |
| if (!boxedStack && data.box) { |
| boxedStack = filteredStack.slice(1); |
| location = location || boxedStack[0]; |
| } |
| location = location || filteredStack[0]; |
| const step = { |
| ...data, |
| stepId, |
| group: parentStep?.group ?? data.group, |
| boxedStack, |
| location, |
| steps: [], |
| attachmentIndices: [], |
| info: new TestStepInfoImpl(this, stepId, data.title, parentStep?.info), |
| complete: (result) => { |
| if (step.endWallTime) |
| return; |
| step.endWallTime = Date.now(); |
| if (result.attachments) { |
| for (const attachment of result.attachments) |
| this._attach(attachment, stepId); |
| } |
| if (result.error) { |
| if (typeof result.error === "object" && !result.error?.[stepSymbol]) |
| result.error[stepSymbol] = step; |
| const error = testInfoError(result.error); |
| if (step.boxedStack) |
| error.stack = `${error.message} |
| ${stringifyStackFrames2(step.boxedStack).join("\n")}`; |
| step.error = error; |
| } |
| if (result.softError) { |
| step.infectParentStepsWithError = true; |
| this._failWithError(result.softError); |
| } |
| if (result.shouldNotRetryTest) |
| this._hasNonRetriableError = true; |
| if (!step.error) { |
| for (const childStep of step.steps) { |
| if (childStep.error && childStep.infectParentStepsWithError) { |
| step.error = childStep.error; |
| step.infectParentStepsWithError = true; |
| break; |
| } |
| } |
| } |
| if (!step.group) { |
| const payload = { |
| testId: this.testId, |
| stepId, |
| wallTime: step.endWallTime, |
| error: step.error ? import_common2.ipc.toTestInfoErrorPayload(step.error) : void 0, |
| suggestedRebaseline: result.suggestedRebaseline, |
| annotations: step.info.annotations |
| }; |
| this._callbacks.onStepEnd(payload); |
| } |
| if (step.group !== "internal") { |
| const errorForTrace = step.error ? { name: "", message: step.error.message || "", stack: step.error.stack } : void 0; |
| const attachments = step.attachmentIndices.map((i) => this.attachments[i]); |
| this._tracing.appendAfterActionForStep(stepId, errorForTrace, attachments, step.info.annotations); |
| } |
| } |
| }; |
| const parentStepList = parentStep ? parentStep.steps : this._steps; |
| parentStepList.push(step); |
| this._stepMap.set(stepId, step); |
| if (!step.group) { |
| const payload = { |
| testId: this.testId, |
| stepId, |
| parentStepId: parentStep ? parentStep.stepId : void 0, |
| title: step.title, |
| category: step.category, |
| wallTime: Date.now(), |
| location: step.location |
| }; |
| this._callbacks.onStepBegin(payload); |
| } |
| if (step.group !== "internal") { |
| this._tracing.appendBeforeActionForStep({ |
| stepId, |
| parentId: parentStep?.stepId, |
| title: step.shortTitle ?? step.title, |
| category: step.category, |
| params: step.params, |
| stack: step.location ? [step.location] : [], |
| group: step.group |
| }); |
| } |
| return step; |
| } |
| _abort(location, message) { |
| this.annotations.push({ type: "abort", description: message, location }); |
| throw new TestAbortError("Test aborted" + (message ? ": " + message : "")); |
| } |
| _interrupt() { |
| this._interruptedPromise.resolve(); |
| this._timeoutManager.interrupt(); |
| if (this.status === "passed") |
| this.status = "interrupted"; |
| } |
| _failWithError(error) { |
| if (this.status === "passed" || this.status === "skipped") |
| this.status = error instanceof TimeoutManagerError ? "timedOut" : "failed"; |
| const serialized = testInfoError(error); |
| const step = typeof error === "object" ? error?.[stepSymbol] : void 0; |
| if (step && step.boxedStack) |
| serialized.stack = `${error.name}: ${error.message} |
| ${stringifyStackFrames2(step.boxedStack).join("\n")}`; |
| this.errors.push(serialized); |
| this._tracing.appendForError(serialized); |
| } |
| async _runAsStep(stepInfo, cb) { |
| const step = this._addStep(stepInfo); |
| try { |
| await cb(); |
| step.complete({}); |
| } catch (error) { |
| step.complete({ error }); |
| throw error; |
| } |
| } |
| async _runWithTimeout(runnable, cb) { |
| try { |
| await this._timeoutManager.withRunnable(runnable, async () => { |
| try { |
| await cb(); |
| } catch (e) { |
| if (this._allowSkips && e instanceof TestSkipError) { |
| if (this.status === "passed") |
| this.status = "skipped"; |
| } else { |
| this._failWithError(e); |
| } |
| throw e; |
| } |
| }); |
| } catch (error) { |
| if (!this._interruptedPromise.isDone() && error instanceof TimeoutManagerError) |
| this._failWithError(error); |
| throw error; |
| } |
| } |
| _isFailure() { |
| return this.status !== "skipped" && this.status !== this.expectedStatus; |
| } |
| _currentHookType() { |
| const type = this._timeoutManager.currentSlotType(); |
| return ["beforeAll", "afterAll", "beforeEach", "afterEach"].includes(type) ? type : void 0; |
| } |
| _setIgnoreTimeouts(ignoreTimeouts) { |
| this._ignoreTimeoutsCounter += ignoreTimeouts ? 1 : -1; |
| this._timeoutManager.setIgnoreTimeouts(this._ignoreTimeoutsCounter > 0); |
| } |
| async _didFinishTestFunction() { |
| const shouldPause = this._workerParams.pauseAtEnd && !this._isFailure() || this._workerParams.pauseOnError && this._isFailure(); |
| if (shouldPause) { |
| await Promise.race([ |
| this._callbacks.onTestPaused({ testId: this.testId, errors: this._isFailure() ? this.errors.map(import_common2.ipc.toTestInfoErrorPayload) : [], status: this.status }), |
| this._interruptedPromise |
| ]); |
| } |
| for (const cb of this._onDidFinishTestFunctionCallbacks) |
| await cb(); |
| } |
| |
| async attach(name, options2 = {}) { |
| const step = this._addStep({ |
| title: `Attach ${escapeWithQuotes2(name, '"')}`, |
| category: "test.attach" |
| }); |
| this._attach( |
| await normalizeAndSaveAttachment(this.outputPath(), name, options2), |
| step.stepId |
| ); |
| step.complete({}); |
| } |
| _attach(attachment, stepId) { |
| const index = this._attachmentsPush(attachment) - 1; |
| let step = stepId ? this._stepMap.get(stepId) : void 0; |
| if (!!step?.group) |
| step = void 0; |
| if (step) { |
| step.attachmentIndices.push(index); |
| } else { |
| const stepId2 = `attach@${createGuid2()}`; |
| this._tracing.appendBeforeActionForStep({ stepId: stepId2, title: `Attach ${escapeWithQuotes2(attachment.name, '"')}`, category: "test.attach", stack: [] }); |
| this._tracing.appendAfterActionForStep(stepId2, void 0, [attachment]); |
| } |
| this._callbacks.onAttach({ |
| testId: this.testId, |
| name: attachment.name, |
| contentType: attachment.contentType, |
| path: attachment.path, |
| body: attachment.body?.toString("base64"), |
| stepId: step?.stepId |
| }); |
| } |
| outputPath(...pathSegments) { |
| const outputPath = this._getOutputPath(...pathSegments); |
| import_fs3.default.mkdirSync(this.outputDir, { recursive: true }); |
| return outputPath; |
| } |
| _getOutputPath(...pathSegments) { |
| const joinedPath = import_path3.default.join(...pathSegments); |
| const outputPath = getContainedPath(this.outputDir, joinedPath); |
| if (outputPath) |
| return outputPath; |
| throw new Error(`The outputPath is not allowed outside of the parent directory. Please fix the defined path. |
| |
| outputPath: ${joinedPath}`); |
| } |
| _fsSanitizedTestName() { |
| const fullTitleWithoutSpec = this.titlePath.slice(1).join(" "); |
| return sanitizeForFilePath2(trimLongString(fullTitleWithoutSpec)); |
| } |
| _resolveSnapshotPaths(kind, name, updateSnapshotIndex, anonymousExtension) { |
| const snapshotNames = kind === "aria" ? this._ariaSnapshotNames : this._snapshotNames; |
| const defaultExtensions = { "aria": ".aria.yml", "screenshot": ".png", "snapshot": ".txt" }; |
| const ariaAwareExtname = (filePath) => kind === "aria" && filePath.endsWith(".aria.yml") ? ".aria.yml" : import_path3.default.extname(filePath); |
| let subPath; |
| let ext; |
| let relativeOutputPath; |
| if (!name) { |
| const index = snapshotNames.lastAnonymousSnapshotIndex + 1; |
| if (updateSnapshotIndex === "updateSnapshotIndex") |
| snapshotNames.lastAnonymousSnapshotIndex = index; |
| const fullTitleWithoutSpec = [...this.titlePath.slice(1), index].join(" "); |
| ext = anonymousExtension ?? defaultExtensions[kind]; |
| subPath = sanitizeFilePathBeforeExtension(trimLongString(fullTitleWithoutSpec) + ext, ext); |
| relativeOutputPath = sanitizeFilePathBeforeExtension(trimLongString(fullTitleWithoutSpec, windowsFilesystemFriendlyLength) + ext, ext); |
| } else { |
| if (Array.isArray(name)) { |
| subPath = import_path3.default.join(...name); |
| relativeOutputPath = import_path3.default.join(...name); |
| ext = ariaAwareExtname(subPath); |
| } else { |
| ext = ariaAwareExtname(name); |
| subPath = sanitizeFilePathBeforeExtension(name, ext); |
| relativeOutputPath = sanitizeFilePathBeforeExtension(trimLongString(name, windowsFilesystemFriendlyLength), ext); |
| } |
| const index = (snapshotNames.lastNamedSnapshotIndex[relativeOutputPath] || 0) + 1; |
| if (updateSnapshotIndex === "updateSnapshotIndex") |
| snapshotNames.lastNamedSnapshotIndex[relativeOutputPath] = index; |
| if (index > 1) |
| relativeOutputPath = addSuffixToFilePath(relativeOutputPath, `-${index - 1}`); |
| } |
| const legacyTemplate = "{snapshotDir}/{testFileDir}/{testFileName}-snapshots/{arg}{-projectName}{-snapshotSuffix}{ext}"; |
| let template; |
| if (kind === "screenshot") { |
| template = this._projectInternal.expect?.toHaveScreenshot?.pathTemplate || this._projectInternal.snapshotPathTemplate || legacyTemplate; |
| } else if (kind === "aria") { |
| const ariaDefaultTemplate = "{snapshotDir}/{testFileDir}/{testFileName}-snapshots/{arg}{ext}"; |
| template = this._projectInternal.expect?.toMatchAriaSnapshot?.pathTemplate || this._projectInternal.snapshotPathTemplate || ariaDefaultTemplate; |
| } else { |
| template = this._projectInternal.snapshotPathTemplate || legacyTemplate; |
| } |
| const nameArgument = import_path3.default.join(import_path3.default.dirname(subPath), import_path3.default.basename(subPath, ext)); |
| const absoluteSnapshotPath = this._applyPathTemplate(template, nameArgument, ext); |
| return { absoluteSnapshotPath, relativeOutputPath }; |
| } |
| _applyPathTemplate(template, nameArgument, ext) { |
| const relativeTestFilePath = import_path3.default.relative(this.project.testDir, this._requireFile); |
| const parsedRelativeTestFilePath = import_path3.default.parse(relativeTestFilePath); |
| const projectNamePathSegment = sanitizeForFilePath2(this.project.name); |
| const snapshotPath = template.replace(/\{(.)?testDir\}/g, "$1" + this.project.testDir).replace(/\{(.)?snapshotDir\}/g, "$1" + this.project.snapshotDir).replace(/\{(.)?snapshotSuffix\}/g, this.snapshotSuffix ? "$1" + this.snapshotSuffix : "").replace(/\{(.)?testFileDir\}/g, "$1" + parsedRelativeTestFilePath.dir).replace(/\{(.)?platform\}/g, "$1" + process.platform).replace(/\{(.)?projectName\}/g, projectNamePathSegment ? "$1" + projectNamePathSegment : "").replace(/\{(.)?testName\}/g, "$1" + this._fsSanitizedTestName()).replace(/\{(.)?testFileBaseName\}/g, "$1" + parsedRelativeTestFilePath.name).replace(/\{(.)?testFileName\}/g, "$1" + parsedRelativeTestFilePath.base).replace(/\{(.)?testFilePath\}/g, "$1" + relativeTestFilePath).replace(/\{(.)?arg\}/g, "$1" + nameArgument).replace(/\{(.)?ext\}/g, ext ? "$1" + ext : ""); |
| return import_path3.default.normalize(import_path3.default.resolve(this._configInternal.configDir, snapshotPath)); |
| } |
| snapshotPath(...args) { |
| let name = args; |
| let kind = "snapshot"; |
| const options2 = args[args.length - 1]; |
| if (options2 && typeof options2 === "object") { |
| kind = options2.kind ?? kind; |
| name = args.slice(0, -1); |
| } |
| if (!["snapshot", "screenshot", "aria"].includes(kind)) |
| throw new Error(`testInfo.snapshotPath: unknown kind "${kind}", must be one of "snapshot", "screenshot" or "aria"`); |
| return this._resolveSnapshotPaths(kind, name.length <= 1 ? name[0] : name, "dontUpdateSnapshotIndex").absoluteSnapshotPath; |
| } |
| setTimeout(timeout) { |
| this._timeoutManager.setTimeout(timeout); |
| } |
| artifactsDir() { |
| return this._workerParams.artifactsDir; |
| } |
| }; |
| var TestStepInfoImpl = class { |
| constructor(testInfo, stepId, title, parentStep) { |
| this.annotations = []; |
| this._testInfo = testInfo; |
| this._stepId = stepId; |
| this._title = title; |
| this._parentStep = parentStep; |
| this.skip = import_common2.transform.wrapFunctionWithLocation((location, ...args) => { |
| if (args.length > 0 && !args[0]) |
| return; |
| const description = args[1]; |
| this.annotations.push({ type: "skip", description, location }); |
| throw new StepSkipError(description); |
| }); |
| } |
| async _runStepBody(skip, body, location) { |
| if (skip) { |
| this.annotations.push({ type: "skip", location }); |
| return void 0; |
| } |
| try { |
| return await body(this); |
| } catch (e) { |
| if (e instanceof StepSkipError) |
| return void 0; |
| throw e; |
| } |
| } |
| async attach(name, options2) { |
| this._testInfo._attach(await normalizeAndSaveAttachment(this._testInfo.outputPath(), name, options2), this._stepId); |
| } |
| get titlePath() { |
| const parent = this._parentStep ?? this._testInfo; |
| return [...parent.titlePath, this._title]; |
| } |
| }; |
| var TestSkipError = class extends Error { |
| }; |
| var TestAbortError = class extends Error { |
| }; |
| var StepSkipError = class extends Error { |
| }; |
| var stepSymbol = Symbol("step"); |
|
|
| |
| var colors2 = require("playwright-core/lib/utilsBundle").colors; |
| var { ManualPromise: ManualPromise5 } = require("playwright-core/lib/coreBundle").iso; |
| var { removeFolders } = require("playwright-core/lib/coreBundle").utils; |
| var { gracefullyCloseAll } = require("playwright-core/lib/coreBundle").utils; |
| var WorkerMain = class extends import_common3.ProcessRunner { |
| constructor(params) { |
| super(); |
| |
| this._fatalErrors = []; |
| |
| this._didRunFullCleanup = false; |
| |
| |
| this._stoppedDueToUnhandledErrorInTestFail = false; |
| |
| this._isStopped = false; |
| |
| this._runFinished = new ManualPromise5(); |
| this._currentTest = null; |
| this._lastRunningTests = []; |
| this._totalRunningTests = 0; |
| |
| |
| |
| this._activeSuites = new Map(); |
| process.env.TEST_WORKER_INDEX = String(params.workerIndex); |
| process.env.TEST_PARALLEL_INDEX = String(params.parallelIndex); |
| globals.setIsWorkerProcess(); |
| this._params = params; |
| this._fixtureRunner = new FixtureRunner(); |
| this._runFinished.resolve(); |
| process.on("unhandledRejection", (reason) => this.unhandledError(reason)); |
| process.on("uncaughtException", (error) => this.unhandledError(error)); |
| process.stdout.write = (chunk, cb) => { |
| this.dispatchEvent("stdOut", import_common3.ipc.stdioChunkToParams(chunk)); |
| this._currentTest?._tracing.appendStdioToTrace("stdout", chunk); |
| if (typeof cb === "function") |
| process.nextTick(cb); |
| return true; |
| }; |
| if (!process.env.PW_RUNNER_DEBUG) { |
| process.stderr.write = (chunk, cb) => { |
| this.dispatchEvent("stdErr", import_common3.ipc.stdioChunkToParams(chunk)); |
| this._currentTest?._tracing.appendStdioToTrace("stderr", chunk); |
| if (typeof cb === "function") |
| process.nextTick(cb); |
| return true; |
| }; |
| } |
| } |
| _stop() { |
| if (!this._isStopped) { |
| this._isStopped = true; |
| this._currentTest?._interrupt(); |
| } |
| return this._runFinished; |
| } |
| async gracefullyClose() { |
| try { |
| await this._stop(); |
| if (!this._config) { |
| return; |
| } |
| const fakeTestInfo = new TestInfoImpl(this._config, this._project, this._params, void 0, 0, emtpyTestInfoCallbacks); |
| const runnable = { type: "teardown" }; |
| await fakeTestInfo._runWithTimeout(runnable, () => this._loadIfNeeded()).catch(() => { |
| }); |
| await this._fixtureRunner.teardownScope("test", fakeTestInfo, runnable).catch(() => { |
| }); |
| await this._fixtureRunner.teardownScope("worker", fakeTestInfo, runnable).catch(() => { |
| }); |
| await fakeTestInfo._runWithTimeout(runnable, () => gracefullyCloseAll()).catch(() => { |
| }); |
| this._fatalErrors.push(...fakeTestInfo.errors); |
| } catch (e) { |
| this._fatalErrors.push(testInfoError(e)); |
| } |
| if (this._fatalErrors.length) { |
| this._appendProcessTeardownDiagnostics(this._fatalErrors[this._fatalErrors.length - 1]); |
| const payload = { fatalErrors: this._fatalErrors.map(import_common3.ipc.toTestInfoErrorPayload) }; |
| this.dispatchEvent("teardownErrors", payload); |
| } |
| } |
| _appendProcessTeardownDiagnostics(error) { |
| if (!this._lastRunningTests.length) |
| return; |
| const count = this._totalRunningTests === 1 ? "1 test" : `${this._totalRunningTests} tests`; |
| let lastMessage = ""; |
| if (this._lastRunningTests.length < this._totalRunningTests) |
| lastMessage = `, last ${this._lastRunningTests.length} tests were`; |
| const message = [ |
| "", |
| "", |
| colors2.red(`Failed worker ran ${count}${lastMessage}:`), |
| ...this._lastRunningTests.map((test) => formatTestTitle(test, this._project.project.name)) |
| ].join("\n"); |
| if (error.message) { |
| if (error.stack) { |
| let index = error.stack.indexOf(error.message); |
| if (index !== -1) { |
| index += error.message.length; |
| error.stack = error.stack.substring(0, index) + message + error.stack.substring(index); |
| } |
| } |
| error.message += message; |
| } else if (error.value) { |
| error.value += message; |
| } |
| } |
| unhandledError(error) { |
| if (!this._currentTest) { |
| if (!this._fatalErrors.length) |
| this._fatalErrors.push(testInfoError(error)); |
| void this._stop(); |
| return; |
| } |
| if (!this._currentTest._hasUnhandledError) { |
| this._currentTest._hasUnhandledError = true; |
| this._currentTest._failWithError(error); |
| } |
| const isExpectError = error instanceof Error && !!error.matcherResult; |
| const shouldContinueInThisWorker = this._currentTest.expectedStatus === "failed" && isExpectError; |
| if (!shouldContinueInThisWorker) { |
| this._stoppedDueToUnhandledErrorInTestFail = true; |
| void this._stop(); |
| } |
| } |
| async _loadIfNeeded() { |
| if (this._config) |
| return; |
| const config = await import_common3.configLoader.deserializeConfig(this._params.config); |
| const project = config.projects.find((p) => p.id === this._params.projectId); |
| if (!project) |
| throw new Error(`Project "${this._params.projectId}" not found in the worker process. Make sure project name does not change.`); |
| this._config = config; |
| this._project = project; |
| this._poolBuilder = import_common3.poolBuilder.PoolBuilder.createForWorker(this._project); |
| this._fixtureRunner.workerFixtureTimeout = this._project.project.timeout; |
| } |
| async runTestGroup(runPayload) { |
| this._runFinished = new ManualPromise5(); |
| const entries = new Map(runPayload.entries.map((e) => [e.testId, e])); |
| let fatalUnknownTestIds; |
| try { |
| await this._loadIfNeeded(); |
| const fileSuite = await import_common3.testLoader.loadTestFile(runPayload.file, this._config); |
| const suite = import_common3.suiteUtils.bindFileSuiteToProject(this._project, fileSuite); |
| if (this._params.repeatEachIndex) |
| import_common3.suiteUtils.applyRepeatEachIndex(this._project, suite, this._params.repeatEachIndex); |
| import_common3.suiteUtils.filterTestsRemoveEmptySuites(suite, (test) => entries.has(test.id)); |
| const tests = suite.allTests(); |
| const unknownTestIds = new Set(entries.keys()); |
| for (const test of tests) |
| unknownTestIds.delete(test.id); |
| if (unknownTestIds.size) { |
| fatalUnknownTestIds = [...unknownTestIds]; |
| void this._stop(); |
| return; |
| } |
| this._poolBuilder.buildPools(suite); |
| this._activeSuites = new Map(); |
| this._didRunFullCleanup = false; |
| for (let i = 0; i < tests.length; i++) { |
| if (this._isStopped && this._didRunFullCleanup) |
| break; |
| const entry = entries.get(tests[i].id); |
| entries.delete(tests[i].id); |
| debugTest(`test started "${tests[i].title}"`); |
| await this._runTest(tests[i], entry.retry, tests[i + 1]); |
| debugTest(`test finished "${tests[i].title}"`); |
| } |
| } catch (e) { |
| this._fatalErrors.push(testInfoError(e)); |
| void this._stop(); |
| } finally { |
| const donePayload = { |
| fatalErrors: this._fatalErrors.map(import_common3.ipc.toTestInfoErrorPayload), |
| skipTestsDueToSetupFailure: [], |
| fatalUnknownTestIds, |
| stoppedDueToUnhandledErrorInTestFail: this._stoppedDueToUnhandledErrorInTestFail |
| }; |
| for (const test of this._skipRemainingTestsInSuite?.allTests() || []) { |
| if (entries.has(test.id)) |
| donePayload.skipTestsDueToSetupFailure.push(test.id); |
| } |
| this.dispatchEvent("done", donePayload); |
| this._fatalErrors = []; |
| this._skipRemainingTestsInSuite = void 0; |
| this._runFinished.resolve(); |
| } |
| } |
| async customMessage(payload) { |
| try { |
| if (this._currentTest?.testId !== payload.testId) |
| throw new Error("Test has already stopped"); |
| const response = await this._currentTest._onCustomMessageCallback?.(payload.request); |
| return { response }; |
| } catch (error) { |
| return { response: {}, error: import_common3.ipc.toTestInfoErrorPayload(testInfoError(error)) }; |
| } |
| } |
| resume(payload) { |
| this._resumePromise?.resolve(payload); |
| } |
| async _runTest(test, retry, nextTest) { |
| const testInfo = new TestInfoImpl(this._config, this._project, this._params, test, retry, { |
| onStepBegin: (payload) => this.dispatchEvent("stepBegin", payload), |
| onStepEnd: (payload) => this.dispatchEvent("stepEnd", payload), |
| onAttach: (payload) => this.dispatchEvent("attach", payload), |
| onTestPaused: (payload) => { |
| this._resumePromise = new ManualPromise5(); |
| this.dispatchEvent("testPaused", payload); |
| return this._resumePromise; |
| } |
| }); |
| const processAnnotation = (annotation) => { |
| testInfo.annotations.push(annotation); |
| switch (annotation.type) { |
| case "fixme": |
| case "skip": |
| testInfo.expectedStatus = "skipped"; |
| break; |
| case "fail": |
| if (testInfo.expectedStatus !== "skipped") |
| testInfo.expectedStatus = "failed"; |
| break; |
| case "slow": |
| testInfo._timeoutManager.slow(); |
| break; |
| } |
| }; |
| if (!this._isStopped) |
| this._fixtureRunner.setPool(test._pool); |
| const suites = getSuites(test); |
| const reversedSuites = suites.slice().reverse(); |
| const nextSuites = new Set(getSuites(nextTest)); |
| testInfo._timeoutManager.setTimeout(test.timeout); |
| for (const annotation of test.annotations) |
| processAnnotation(annotation); |
| for (const suite of suites) { |
| const extraAnnotations = this._activeSuites.get(suite) || []; |
| for (const annotation of extraAnnotations) |
| processAnnotation(annotation); |
| } |
| this._currentTest = testInfo; |
| globals.setCurrentTestInfo(testInfo); |
| (0, import_expect.setExpectConfig)({ |
| testInfo, |
| filteredStackTrace, |
| ignoreSnapshots: testInfo._projectInternal.project.ignoreSnapshots, |
| updateSnapshots: testInfo.config.updateSnapshots, |
| timeout: testInfo._projectInternal.expect?.timeout, |
| toHaveScreenshot: testInfo._projectInternal.expect?.toHaveScreenshot, |
| toMatchSnapshot: testInfo._projectInternal.expect?.toMatchSnapshot, |
| toMatchAriaSnapshot: testInfo._projectInternal.expect?.toMatchAriaSnapshot, |
| toPass: testInfo._projectInternal.expect?.toPass |
| }); |
| this.dispatchEvent("testBegin", buildTestBeginPayload(testInfo)); |
| const isSkipped = testInfo.expectedStatus === "skipped"; |
| const hasAfterAllToRunBeforeNextTest = reversedSuites.some((suite) => { |
| return this._activeSuites.has(suite) && !nextSuites.has(suite) && suite._hooks.some((hook) => hook.type === "afterAll"); |
| }); |
| if (isSkipped && nextTest && !hasAfterAllToRunBeforeNextTest) { |
| testInfo.status = "skipped"; |
| this.dispatchEvent("testEnd", buildTestEndPayload(testInfo)); |
| return; |
| } |
| this._totalRunningTests++; |
| this._lastRunningTests.push(test); |
| if (this._lastRunningTests.length > 10) |
| this._lastRunningTests.shift(); |
| let shouldRunAfterEachHooks = false; |
| testInfo._allowSkips = true; |
| await (async () => { |
| await testInfo._runWithTimeout({ type: "test" }, async () => { |
| const traceFixtureRegistration = test._pool.resolve("trace"); |
| if (!traceFixtureRegistration) |
| return; |
| if (typeof traceFixtureRegistration.fn === "function") |
| throw new Error(`"trace" option cannot be a function`); |
| await testInfo._tracing.startIfNeeded(traceFixtureRegistration.fn); |
| }); |
| if (this._isStopped || isSkipped) { |
| testInfo.status = "skipped"; |
| return; |
| } |
| await removeFolders([testInfo.outputDir]); |
| let testFunctionParams = null; |
| await testInfo._runAsStep({ title: "Before Hooks", category: "hook" }, async () => { |
| for (const suite of suites) |
| await this._runBeforeAllHooksForSuite(suite, testInfo); |
| shouldRunAfterEachHooks = true; |
| await this._runEachHooksForSuites(suites, "beforeEach", testInfo); |
| const params = await this._fixtureRunner.resolveParametersForFunction(test.fn, testInfo, "test", { type: "test" }); |
| if (params !== null) |
| testFunctionParams = params.result; |
| }); |
| if (testFunctionParams === null) { |
| return; |
| } |
| await testInfo._runWithTimeout({ type: "test" }, async () => { |
| const fn = test.fn; |
| await fn(testFunctionParams, testInfo); |
| }); |
| })().catch(() => { |
| }); |
| testInfo.duration = testInfo._timeoutManager.defaultSlot().elapsed | 0; |
| testInfo._allowSkips = true; |
| const afterHooksTimeout = calculateMaxTimeout(this._project.project.timeout, testInfo.timeout); |
| const afterHooksSlot = { timeout: afterHooksTimeout, elapsed: 0 }; |
| await testInfo._runAsStep({ title: "After Hooks", category: "hook" }, async () => { |
| let firstAfterHooksError; |
| try { |
| await testInfo._runWithTimeout({ type: "test", slot: afterHooksSlot }, () => testInfo._didFinishTestFunction()); |
| } catch (error) { |
| firstAfterHooksError = firstAfterHooksError ?? error; |
| } |
| try { |
| if (shouldRunAfterEachHooks) |
| await this._runEachHooksForSuites(reversedSuites, "afterEach", testInfo, afterHooksSlot); |
| } catch (error) { |
| firstAfterHooksError = firstAfterHooksError ?? error; |
| } |
| testInfo._tracing.didFinishTestFunctionAndAfterEachHooks(); |
| try { |
| await this._fixtureRunner.teardownScope("test", testInfo, { type: "test", slot: afterHooksSlot }); |
| } catch (error) { |
| firstAfterHooksError = firstAfterHooksError ?? error; |
| } |
| for (const suite of reversedSuites) { |
| if (!nextSuites.has(suite) || testInfo._isFailure()) { |
| try { |
| await this._runAfterAllHooksForSuite(suite, testInfo); |
| } catch (error) { |
| firstAfterHooksError = firstAfterHooksError ?? error; |
| } |
| } |
| } |
| if (firstAfterHooksError) |
| throw firstAfterHooksError; |
| }).catch(() => { |
| }); |
| if (testInfo._isFailure()) |
| this._isStopped = true; |
| if (this._isStopped) { |
| this._didRunFullCleanup = true; |
| await testInfo._runAsStep({ title: "Worker Cleanup", category: "hook" }, async () => { |
| let firstWorkerCleanupError; |
| const teardownSlot = { timeout: this._project.project.timeout, elapsed: 0 }; |
| try { |
| await this._fixtureRunner.teardownScope("test", testInfo, { type: "test", slot: teardownSlot }); |
| } catch (error) { |
| firstWorkerCleanupError = firstWorkerCleanupError ?? error; |
| } |
| for (const suite of reversedSuites) { |
| try { |
| await this._runAfterAllHooksForSuite(suite, testInfo); |
| } catch (error) { |
| firstWorkerCleanupError = firstWorkerCleanupError ?? error; |
| } |
| } |
| try { |
| await this._fixtureRunner.teardownScope("worker", testInfo, { type: "teardown", slot: teardownSlot }); |
| } catch (error) { |
| firstWorkerCleanupError = firstWorkerCleanupError ?? error; |
| } |
| if (firstWorkerCleanupError) |
| throw firstWorkerCleanupError; |
| }).catch(() => { |
| }); |
| } |
| const tracingSlot = { timeout: this._project.project.timeout, elapsed: 0 }; |
| await testInfo._runWithTimeout({ type: "test", slot: tracingSlot }, async () => { |
| await testInfo._tracing.stopIfNeeded(); |
| }).catch(() => { |
| }); |
| testInfo.duration = testInfo._timeoutManager.defaultSlot().elapsed + afterHooksSlot.elapsed | 0; |
| this._currentTest = null; |
| globals.setCurrentTestInfo(null); |
| (0, import_expect.setExpectConfig)({ testInfo: null, filteredStackTrace, ignoreSnapshots: false, updateSnapshots: "missing" }); |
| this.dispatchEvent("testEnd", buildTestEndPayload(testInfo)); |
| const preserveOutput = this._config.config.preserveOutput === "always" || this._config.config.preserveOutput === "failures-only" && testInfo._isFailure(); |
| if (!preserveOutput) |
| await removeFolders([testInfo.outputDir]); |
| } |
| _collectHooksAndModifiers(suite, type, testInfo) { |
| const runnables = []; |
| for (const modifier of suite._modifiers) { |
| const modifierType = this._fixtureRunner.dependsOnWorkerFixturesOnly(modifier.fn, modifier.location) ? "beforeAll" : "beforeEach"; |
| if (modifierType !== type) |
| continue; |
| const fn = async (fixtures3) => { |
| const result = await modifier.fn(fixtures3); |
| testInfo._modifier(modifier.type, modifier.location, [!!result, modifier.description]); |
| }; |
| import_common3.fixtures.inheritFixtureNames(modifier.fn, fn); |
| runnables.push({ |
| title: `${modifier.type} modifier`, |
| location: modifier.location, |
| type: modifier.type, |
| fn |
| }); |
| } |
| runnables.push(...suite._hooks.filter((hook) => hook.type === type)); |
| return runnables; |
| } |
| async _runBeforeAllHooksForSuite(suite, testInfo) { |
| if (this._activeSuites.has(suite)) |
| return; |
| const extraAnnotations = []; |
| this._activeSuites.set(suite, extraAnnotations); |
| await this._runAllHooksForSuite(suite, testInfo, "beforeAll", extraAnnotations); |
| } |
| async _runAllHooksForSuite(suite, testInfo, type, extraAnnotations) { |
| let firstError; |
| for (const hook of this._collectHooksAndModifiers(suite, type, testInfo)) { |
| try { |
| await testInfo._runAsStep({ title: hook.title, category: "hook", location: hook.location }, async () => { |
| const timeSlot = { timeout: this._project.project.timeout, elapsed: 0 }; |
| const runnable = { type: hook.type, slot: timeSlot, location: hook.location }; |
| const existingAnnotations = new Set(testInfo.annotations); |
| try { |
| await this._fixtureRunner.resolveParametersAndRunFunction(hook.fn, testInfo, "all-hooks-only", runnable); |
| } finally { |
| if (extraAnnotations) { |
| const newAnnotations = testInfo.annotations.filter((a) => !existingAnnotations.has(a)); |
| extraAnnotations.push(...newAnnotations); |
| } |
| await this._fixtureRunner.teardownScope("test", testInfo, runnable); |
| } |
| }); |
| } catch (error) { |
| firstError = firstError ?? error; |
| if (type === "beforeAll" && error instanceof TestSkipError) |
| break; |
| if (type === "beforeAll" && !this._skipRemainingTestsInSuite) { |
| this._skipRemainingTestsInSuite = suite; |
| } |
| } |
| } |
| if (firstError) |
| throw firstError; |
| } |
| async _runAfterAllHooksForSuite(suite, testInfo) { |
| if (!this._activeSuites.has(suite)) |
| return; |
| this._activeSuites.delete(suite); |
| await this._runAllHooksForSuite(suite, testInfo, "afterAll"); |
| } |
| async _runEachHooksForSuites(suites, type, testInfo, slot) { |
| let firstError; |
| const hooks = suites.map((suite) => this._collectHooksAndModifiers(suite, type, testInfo)).flat(); |
| for (const hook of hooks) { |
| const runnable = { type: hook.type, location: hook.location, slot }; |
| if (testInfo._timeoutManager.isTimeExhaustedFor(runnable)) { |
| continue; |
| } |
| try { |
| await testInfo._runAsStep({ title: hook.title, category: "hook", location: hook.location }, async () => { |
| await this._fixtureRunner.resolveParametersAndRunFunction(hook.fn, testInfo, "test", runnable); |
| }); |
| } catch (error) { |
| firstError = firstError ?? error; |
| if (error instanceof TestSkipError) |
| break; |
| } |
| } |
| if (firstError) |
| throw firstError; |
| } |
| }; |
| function buildTestBeginPayload(testInfo) { |
| return { |
| testId: testInfo.testId, |
| startWallTime: testInfo._startWallTime |
| }; |
| } |
| function buildTestEndPayload(testInfo) { |
| return { |
| testId: testInfo.testId, |
| duration: testInfo.duration, |
| status: testInfo.status, |
| errors: testInfo.errors.map(import_common3.ipc.toTestInfoErrorPayload), |
| hasNonRetriableError: testInfo._hasNonRetriableError, |
| expectedStatus: testInfo.expectedStatus, |
| annotations: testInfo.annotations, |
| timeout: testInfo.timeout |
| }; |
| } |
| function getSuites(test) { |
| const suites = []; |
| for (let suite = test?.parent; suite; suite = suite.parent) |
| suites.push(suite); |
| suites.reverse(); |
| return suites; |
| } |
| function formatTestTitle(test, projectName) { |
| const [, ...titles] = test.titlePath(); |
| const location = `${relativeFilePath(test.location.file)}:${test.location.line}:${test.location.column}`; |
| const projectTitle = projectName ? `[${projectName}] \u203A ` : ""; |
| return `${projectTitle}${location} \u203A ${titles.join(" \u203A ")}`; |
| } |
| function calculateMaxTimeout(t1, t2) { |
| return !t1 || !t2 ? 0 : Math.max(t1, t2); |
| } |
| var create = (params) => new WorkerMain(params); |
|
|
| |
| (0, import_common4.startProcessRunner)(create); |
|
|