Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@expressen/tallahassee

Package Overview
Dependencies
Maintainers
12
Versions
207
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@expressen/tallahassee - npm Package Compare versions

Comparing version 11.21.0 to 12.0.0

lib/CustomElementRegistry.js

11

CHANGELOG.md

@@ -6,2 +6,13 @@ Changelog

## 12.0.0
- implement custom element, i.e. `window.customElements.define(name, `
- fix submit from click emitting `PointerEvent` picked up by form element and submitted
- new option for regexp override of `window.matchMedia` mediaQuery
- add `setProperty` to `CSSStyleDeclaration`
- add `submitter` property to SubmitEvent
- fix `target` property of Event
- fix `currentTarget` property of Event
- `MediaQueryList` as class extending EventTarget
## 11.21.0

@@ -8,0 +19,0 @@

2

index.js

@@ -54,3 +54,3 @@ "use strict";

return new WebPage(this[kOrigin], this.jar, requestHeaders);
return new WebPage(this[kOrigin], this.jar, requestHeaders, this.options);
};

@@ -50,2 +50,3 @@ "use strict";

const options = webPage.options;
const window = this.window = new Window(resp, {

@@ -56,3 +57,4 @@ fetch: Fetch(webPage.fetch.bind(webPage)),

},
}, webPage.userAgent);
...(options?.console && {console})
}, webPage.userAgent, options);

@@ -175,3 +177,3 @@ Object.defineProperty(document, "_window", {

const method = form.getAttribute("method") || "GET";
const formaction = (event._submitElement && event._submitElement.getAttribute("formaction")) || form.getAttribute("action");
const formaction = (event.submitter?.getAttribute("formaction")) || form.getAttribute("action");
const action = formaction || this.window.location.pathname + (this.window.location.search ? this.window.location.search : "");

@@ -178,0 +180,0 @@

@@ -90,2 +90,5 @@ "use strict";

}
setProperty(name, value) {
this[name] = value;
}
removeProperty(name) {

@@ -92,0 +95,0 @@ delete this[name];

@@ -9,3 +9,3 @@ "use strict";

const DOMImplementation = require("./DOMImplementation");
const elementFactory = require("./elementFactory");
const ElementFactory = require("./elementFactory");
const HTMLCollection = require("./HTMLCollection");

@@ -22,2 +22,3 @@ const Location = require("./Location");

const fullscreenElementSymbol = Symbol.for("fullscreenElement");
const kElementFactory = Symbol.for("element factory");

@@ -34,2 +35,3 @@ module.exports = class Document extends Node {

this[fullscreenElementSymbol] = null;
this[kElementFactory] = new ElementFactory(this);
}

@@ -96,3 +98,3 @@ get $() {

createElement(elementTagName) {
const element = elementFactory(this, this.$(`<${elementTagName}></${elementTagName}>`));
const element = this[kElementFactory].create(this.$(`<${elementTagName}></${elementTagName}>`));
this[loadedSymbol].push(element);

@@ -113,2 +115,3 @@ return element;

dispatchEvent(event) {
event.path.push(this);
if (event?.type === "fullscreenchange") {

@@ -129,6 +132,5 @@ if (!event.target) return;

exitFullscreen() {
const fullscreenchangeEvent = new Event("fullscreenchange");
fullscreenchangeEvent.target = this[fullscreenElementSymbol];
this.dispatchEvent(fullscreenchangeEvent);
const fullScreenElm = this[fullscreenElementSymbol];
fullScreenElm?.dispatchEvent(new Event("fullscreenchange", { bubbles: true }));
this[fullscreenElementSymbol] = null;
}

@@ -177,3 +179,3 @@ getElementById(id) {

mockElement = elementFactory(this, $elm);
mockElement = this[kElementFactory].create($elm);

@@ -180,0 +182,0 @@ loaded.push(mockElement);

"use strict";
const { DOCUMENT_FRAGMENT_NODE, TEXT_NODE } = require("./nodeTypes");
const { Event } = require("./Events");
const { Event, PointerEvent } = require("./Events");
const { getElementsByClassName, getElementsByTagName } = require("./getElements");

@@ -254,3 +254,3 @@ const Attr = require("./Attr");

this.dispatchEvent(new Event("click", { bubbles: true }));
this.dispatchEvent(new PointerEvent("click", { bubbles: true }));
}

@@ -405,5 +405,3 @@ closest(selector) {

const fullscreenchangeEvent = new Event("fullscreenchange", { bubbles: true });
fullscreenchangeEvent.target = this;
this.ownerDocument.dispatchEvent(fullscreenchangeEvent);
this.dispatchEvent(fullscreenchangeEvent);
}

@@ -410,0 +408,0 @@ cloneNode(deep) {

@@ -19,35 +19,46 @@ "use strict";

module.exports = function elementFactory(document, $elm) {
module.exports = ElementFactory;
function ElementFactory(document) {
this.document = document;
this.definitions = {
a: HTMLAnchorElement,
button: HTMLButtonElement,
dialog: HTMLDialogElement,
form: HTMLFormElement,
input: HTMLInputElement,
video: HTMLVideoElement,
template: HTMLTemplateElement,
textarea: HTMLTextAreaElement,
script: HTMLScriptElement,
select: HTMLSelectElement,
option: HTMLOptionElement,
"!doctype": DocumentType,
};
this.custom = {};
}
ElementFactory.prototype.create = function define($elm) {
const nodeType = $elm[0].nodeType;
const document = this.document;
if (nodeType === TEXT_NODE) return new Text(document, $elm);
const tagName = ($elm[0]?.name || "").toLowerCase();
const definitions = this.definitions;
if (tagName in definitions) return new definitions[tagName](document, $elm);
const custom = this.custom;
if (tagName in custom) {
const elm = new custom[tagName](document, $elm);
if (elm.connectedCallback) elm.connectedCallback();
return elm;
}
switch (tagName) {
case "a":
return new HTMLAnchorElement(document, $elm);
case "button":
return new HTMLButtonElement(document, $elm);
case "dialog":
return new HTMLDialogElement(document, $elm);
case "form":
return new HTMLFormElement(document, $elm);
case "input":
return new HTMLInputElement(document, $elm);
case "video":
return new HTMLVideoElement(document, $elm);
case "template":
return new HTMLTemplateElement(document, $elm);
case "textarea":
return new HTMLTextAreaElement(document, $elm);
case "script":
return new HTMLScriptElement(document, $elm);
case "select":
return new HTMLSelectElement(document, $elm);
case "option":
return new HTMLOptionElement(document, $elm);
case "!doctype":
return new DocumentType(document, $elm);
default:
return new Element(document, $elm);
}
return new Element(document, $elm);
};
ElementFactory.prototype.define = function define(tagName, TagClass) {
this.custom[tagName] = TagClass;
};
ElementFactory.prototype.get = function get(name) {
return this.custom[name];
};

@@ -6,2 +6,3 @@ "use strict";

const detailSymbol = Symbol.for("detail");
const kSubmitter = Symbol.for("submitter");

@@ -22,2 +23,8 @@ class Event {

}
get currentTarget() {
return this.path[this.path.length - 1];
}
get target() {
return this.path[0];
}
preventDefault() {

@@ -54,6 +61,23 @@ this[defaultPreventedSymbol] = true;

class SubmitEvent extends Event {
constructor(type, options = {bubbles: true}) {
super(type, options);
this[kSubmitter] = undefined;
}
get submitter() {
return this[kSubmitter];
}
}
class PointerEvent extends Event {}
module.exports = {
Event,
CustomEvent,
InputEvent,
CustomEvent
PointerEvent,
SubmitEvent,
symbols: {
submitter: kSubmitter,
},
};

@@ -48,6 +48,2 @@ "use strict";

event.path.push(this);
if (!event.target) {
event.target = this;
}
const eventName = event.type;

@@ -54,0 +50,0 @@ this[kEmitter].emit(eventName, event);

"use strict";
const { Event } = require("./Events");
const { PointerEvent } = require("./Events");
const Element = require("./Element");

@@ -25,19 +25,5 @@

if (this.disabled) return;
const clickEvent = new Event("click", { bubbles: true });
const clickEvent = new PointerEvent("click", { bubbles: true });
this.dispatchEvent(clickEvent);
if (clickEvent.defaultPrevented) return;
if (!this.form) return;
if (!this.type || this.type === "submit") {
const submitEvent = new Event("submit", { bubbles: true });
submitEvent._submitElement = this;
if (this.form.reportValidity()) {
this.form.dispatchEvent(submitEvent);
}
} else if (this.type === "reset") {
this.form.reset();
}
}
};
"use strict";
const { Event } = require("./Events");
const { Event, SubmitEvent, symbols } = require("./Events");
const Element = require("./Element");

@@ -63,2 +63,16 @@ const HTMLFormControlsCollection = require("./HTMLFormControlsCollection");

}
dispatchEvent(event) {
super.dispatchEvent(event);
const target = event.target;
if (!target || target === this || event.type !== "click") return;
if (target.type === "submit" || (target.tagName === "BUTTON" && !target.type)) {
if (!this.reportValidity()) return;
const submitEvent = new SubmitEvent("submit");
submitEvent[symbols.submitter] = event.target;
super.dispatchEvent(submitEvent);
} else if (target.type === "reset") {
this.reset();
}
}
submit() {

@@ -65,0 +79,0 @@ this.dispatchEvent(new Event("_form_submit", { bubbles: true }));

"use strict";
const { Event, InputEvent } = require("./Events");
const { Event, InputEvent, PointerEvent } = require("./Events");
const Element = require("./Element");

@@ -60,3 +60,3 @@

if (this.disabled) return;
const clickEvent = new Event("click", { bubbles: true });
const clickEvent = new PointerEvent("click", { bubbles: true });
const type = this.type;

@@ -63,0 +63,0 @@

"use strict";
const {EventEmitter} = require("events");
const EventTarget = require("./EventTarget");
module.exports = function MediaQueryList(mediaQuery) {
if (mediaQuery === undefined) throw new TypeError("Failed to execute 'matchMedia' on 'Window': 1 argument required, but only 0 present.");
const kWindow = Symbol.for("window");
const kMedia = Symbol.for("media");
const kMatches = Symbol.for("matches");
const kOverride = Symbol.for("match media override");
const window = this;
module.exports = class MediaQueryList extends EventTarget {
constructor(window, mediaQuery, overrideMatchMedia) {
if (mediaQuery === undefined) throw new TypeError("Failed to execute 'matchMedia' on 'Window': 1 argument required, but only 0 present.");
super();
this[kWindow] = window;
this[kMedia] = mediaQuery;
this[kOverride] = overrideMatchMedia;
this[kMatches] = this._evaluate();
window.styleMedia = window.styleMedia || { type: "screen" };
let matches = evaluate();
const emitter = new EventEmitter();
window.addEventListener("resize", reEvaluate);
return {
media: mediaQuery,
get matches() {
return matches;
},
addListener(callback) {
emitter.on("change", callback);
},
removeListener(callback) {
emitter.off("change", callback);
window.addEventListener("resize", this._reEvaluate.bind(this));
}
get media() {
return this[kMedia];
}
get matches() {
return this[kMatches];
}
_evaluate() {
const mediaQuery = this.media;
if (this[kOverride] instanceof RegExp && this[kOverride].test(mediaQuery)) {
return true;
}
};
function reEvaluate() {
const newMatches = evaluate();
if (matches === newMatches) return;
matches = newMatches;
const mediaQueryListEvent = new window.Event("change");
mediaQueryListEvent.matches = matches;
emitter.emit("change", mediaQueryListEvent);
}
function evaluate() {
const mediaTypes = /^(only\s|any\s|not\s)?(all|screen|print)/.exec(mediaQuery);

@@ -45,3 +37,3 @@ let match = false;

if (mediaTypes) {
match = evaluateMediaTypes(mediaTypes);
match = this._evaluateMediaTypes(mediaTypes);
}

@@ -51,24 +43,32 @@

if (mediaConditions) {
match = evaluateMediaConditions(mediaConditions);
match = this._evaluateMediaConditions(mediaConditions);
}
return match;
}
_evaluateMediaConditions(conditions) {
const window = this[kWindow];
for (let i = 1; i < conditions.length; i++) {
const condition = conditions[i];
const [prop, value] = condition.split(":");
function evaluateMediaConditions(conditions) {
for (let i = 1; i < conditions.length; i++) {
const condition = conditions[i];
const [prop, value] = condition.split(":");
if (prop.startsWith("min")) {
return window.innerWidth >= parseInt(value);
} else if (prop.startsWith("max")) {
return window.innerWidth <= parseInt(value);
}
if (prop.startsWith("min")) {
return window.innerWidth >= parseInt(value);
} else if (prop.startsWith("max")) {
return window.innerWidth <= parseInt(value);
}
}
}
_evaluateMediaTypes(types) {
return types[0] === (this[kWindow].styleMedia?.type || "screen");
}
_reEvaluate() {
const newMatches = this._evaluate();
if (this[kMatches] === newMatches) return;
function evaluateMediaTypes(types) {
return types[0] === window.styleMedia.type;
}
this[kMatches] = newMatches;
const mediaQueryListEvent = new this[kWindow].Event("change");
mediaQueryListEvent.matches = newMatches;
this.dispatchEvent(mediaQueryListEvent);
}
};

@@ -16,3 +16,3 @@ "use strict";

module.exports = class WebPage {
constructor(origin, jar, originRequestHeaders) {
constructor(origin, jar, originRequestHeaders, options) {
this[kOrigin] = origin;

@@ -25,2 +25,3 @@ this.jar = jar;

this.referrer = originRequestHeaders.referer;
this.options = options;
}

@@ -27,0 +28,0 @@ async navigateTo(uri, headers, statusCode = 200) {

@@ -7,3 +7,5 @@ "use strict";

const {performance} = require("perf_hooks");
const CustomElementRegistry = require("./CustomElementRegistry");
const Element = require("./Element");
const HTMLElement = require("./HTMLElement");
const FormData = require("./FormData");

@@ -28,6 +30,10 @@ const History = require("./History");

const windowSizeSymbol = Symbol.for("windowSize");
const kOptions = Symbol.for("browser options");
const kCustomElements = Symbol.for("custom elements");
module.exports = class Window {
constructor(resp, windowObjects = {console}, userAgent) {
constructor(resp, windowObjects = {console}, userAgent, options) {
this[kResponse] = resp;
this[kOptions] = options;
const webPageUrl = windowObjects.location ? url.format(windowObjects.location) : resp.url;

@@ -45,2 +51,3 @@ const location = this[locationSymbol] = new Location(this, webPageUrl);

};
this[kCustomElements] = new CustomElementRegistry(this);
this.atob = atob;

@@ -92,5 +99,11 @@ this.btoa = btoa;

}
get customElements() {
return this[kCustomElements];
}
get Element() {
return Element;
}
get HTMLElement() {
return HTMLElement;
}
get FormData() {

@@ -129,4 +142,4 @@ return FormData;

}
matchMedia(...args) {
return MediaQueryList.call(this, ...args);
matchMedia(mediaQueryString) {
return new MediaQueryList(this, mediaQueryString, this[kOptions]?.matchMedia);
}

@@ -133,0 +146,0 @@ scroll(xCoord, yCoord) {

{
"name": "@expressen/tallahassee",
"version": "11.21.0",
"version": "12.0.0",
"description": "Lightweight client testing framework",

@@ -39,6 +39,6 @@ "main": "index.js",

"chai": "^4.3.6",
"eslint": "^8.24.0",
"express": "^4.18.1",
"eslint": "^8.25.0",
"express": "^4.18.2",
"markdown-toc": "^1.2.0",
"mocha": "^10.0.0",
"mocha": "^10.1.0",
"nock": "^13.2.9"

@@ -45,0 +45,0 @@ },

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc