Skip to content

Commit

Permalink
Merge pull request #75 from ulixee/the-day-the-music-died
Browse files Browse the repository at this point in the history
The day the music died
  • Loading branch information
blakebyrnes authored Jan 25, 2022
2 parents 422f120 + f06d242 commit 507a310
Show file tree
Hide file tree
Showing 14 changed files with 32 additions and 249 deletions.
13 changes: 4 additions & 9 deletions client/lib/FrameEnvironment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,6 @@ import { createInstanceWithNodePointer, getAwaitedPathAsMethodArg } from './Setu
import CoreFrameEnvironment from './CoreFrameEnvironment';
import Tab, { IState as ITabState } from './Tab';
import Resource, { createResource } from './Resource';
import IMagicSelectorOptions from '@ulixee/hero-interfaces/IMagicSelectorOptions';
import {
runMagicSelectorFnName,
runMagicSelectorAllFnName,
} from '@ulixee/hero-interfaces/jsPathFnNames';
import { IMousePositionXY } from '@ulixee/hero-interfaces/IInteractions';

const { getState, setState } = StateMachine<FrameEnvironment, IState>();
Expand Down Expand Up @@ -217,14 +212,14 @@ export default class FrameEnvironment {
return coreFrame.getJsValue<T>(path);
}

public magicSelector(selectorOrOptions?: string | IMagicSelectorOptions): ISuperNode {
const awaitedPath = new AwaitedPath(null, [runMagicSelectorFnName, selectorOrOptions]);
public querySelector(selector: string): ISuperNode {
const awaitedPath = new AwaitedPath(null, 'document', ['querySelector', selector]);
const awaitedOptions: IAwaitedOptions = { coreFrame: getState(this).coreFrame };
return createSuperNode(awaitedPath, awaitedOptions);
}

public magicSelectorAll(selectorOrOptions?: string | IMagicSelectorOptions): ISuperNodeList {
const awaitedPath = new AwaitedPath(null, [runMagicSelectorAllFnName, selectorOrOptions]);
public querySelectorAll(selector: string): ISuperNodeList {
const awaitedPath = new AwaitedPath(null, 'document', ['querySelectorAll', selector]);
const awaitedOptions: IAwaitedOptions = { coreFrame: getState(this).coreFrame };
return createSuperNodeList(awaitedPath, awaitedOptions);
}
Expand Down
9 changes: 4 additions & 5 deletions client/lib/Hero.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ import CoreFrameEnvironment from './CoreFrameEnvironment';
import ConnectionManager from './ConnectionManager';
import './DomExtender';
import IPageStateDefinitions from '../interfaces/IPageStateDefinitions';
import IMagicSelectorOptions from '@ulixee/hero-interfaces/IMagicSelectorOptions';
import ICollectedResource from '@ulixee/hero-interfaces/ICollectedResource';
import ICollectedFragment from '@ulixee/hero-interfaces/ICollectedFragment';

Expand Down Expand Up @@ -436,12 +435,12 @@ export default class Hero extends AwaitedEventTarget<{
return await this.getComputedVisibility(element as any).then(x => x.isVisible);
}

public magicSelector(selectorOrOptions?: string | IMagicSelectorOptions): ISuperNode {
return this.activeTab.magicSelector(selectorOrOptions);
public querySelector(selector: string): ISuperNode {
return this.activeTab.querySelector(selector);
}

public magicSelectorAll(selectorOrOptions?: string | IMagicSelectorOptions): ISuperNodeList {
return this.activeTab.magicSelectorAll(selectorOrOptions);
public querySelectorAll(selector: string): ISuperNodeList {
return this.activeTab.querySelectorAll(selector);
}

public takeScreenshot(options?: IScreenshotOptions): Promise<Buffer> {
Expand Down
9 changes: 4 additions & 5 deletions client/lib/Tab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ import Dialog from './Dialog';
import FileChooser from './FileChooser';
import PageState from './PageState';
import IPageStateDefinitions from '../interfaces/IPageStateDefinitions';
import IMagicSelectorOptions from '@ulixee/hero-interfaces/IMagicSelectorOptions';

const awaitedPathState = StateMachine<
any,
Expand Down Expand Up @@ -208,12 +207,12 @@ export default class Tab extends AwaitedEventTarget<IEventType> {
return await this.mainFrameEnvironment.getComputedVisibility(node);
}

public magicSelector(selectorOrOptions?: string | IMagicSelectorOptions): ISuperNode {
return this.mainFrameEnvironment.magicSelector(selectorOrOptions);
public querySelector(selector: string): ISuperNode {
return this.mainFrameEnvironment.querySelector(selector);
}

public magicSelectorAll(selectorOrOptions?: string | IMagicSelectorOptions): ISuperNodeList {
return this.mainFrameEnvironment.magicSelectorAll(selectorOrOptions);
public querySelectorAll(selector: string): ISuperNodeList {
return this.mainFrameEnvironment.querySelectorAll(selector);
}

public async takeScreenshot(options?: IScreenshotOptions): Promise<Buffer> {
Expand Down
59 changes: 0 additions & 59 deletions core/injected-scripts/jsPath.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import IExecJsPathResult from '@ulixee/hero-interfaces/IExecJsPathResult';
import type INodePointer from 'awaited-dom/base/INodePointer';
import IElementRect from '@ulixee/hero-interfaces/IElementRect';
import IMagicSelectorOptions from '@ulixee/hero-interfaces/IMagicSelectorOptions';
import IPoint from '@ulixee/hero-interfaces/IPoint';
import { IJsPathError } from '@ulixee/hero-interfaces/IJsPathError';
import { INodeVisibility } from '@ulixee/hero-interfaces/INodeVisibility';
Expand Down Expand Up @@ -235,7 +234,6 @@ class ObjectAtPath {

private _obstructedByElement: Element;
private lookupStep: IPathStep;
private lookupStepMagicSelectorMatches: number[];
private lookupStepIndex = 0;
private nodePointer: INodePointer;

Expand Down Expand Up @@ -422,60 +420,6 @@ class ObjectAtPath {
return this;
}

public magicSelector(options: IMagicSelectorOptions): Node {
const { querySelectors, minMatchingSelectors } = options;
const results: number[] = [];
this.lookupStepMagicSelectorMatches = results;
const candidates = new Map<Node, number>();
for (const selector of querySelectors) {
try {
const result = document.querySelectorAll(selector);
results.push(result.length);
if (result.length === 1) {
const node = result[0];
const count = (candidates.get(node) ?? 0) + 1;
candidates.set(node, count);
if (count >= minMatchingSelectors) {
return node;
}
}
} catch (err) {
results.push(-1);
}
}
return null;
}

public magicSelectorAll(options: IMagicSelectorOptions): NodeList {
const { querySelectors, minMatchingSelectors } = options;
const results: number[] = [];
this.lookupStepMagicSelectorMatches = results;
const countByStringifiedNodeIds = new Map<string, number>();
for (const selector of querySelectors) {
try {
const result = document.querySelectorAll(selector);
results.push(result.length);
const nodeIds: number[] = [];
for (const node of result) {
const nodeId = NodeTracker.watchNode(node);
nodeIds.push(nodeId);
}

const stringifiedIds = nodeIds.toString();
const count = (countByStringifiedNodeIds.get(stringifiedIds) ?? 0) + 1;
countByStringifiedNodeIds.set(stringifiedIds, count);
if (count >= minMatchingSelectors) {
return result;
}
} catch (err) {
results.push(-1);
}
}

// create an empty node list if we didn't match anything
return new DocumentFragment().childNodes;
}

public toReturnError(error: Error): IExecJsPathResult {
const pathError = <IJsPathError>{
error: String(error),
Expand All @@ -484,9 +428,6 @@ class ObjectAtPath {
index: this.lookupStepIndex,
},
};
if (this.lookupStepMagicSelectorMatches) {
pathError.pathState.magicSelectorMatches = this.lookupStepMagicSelectorMatches;
}
return {
value: null,
pathError,
Expand Down
30 changes: 0 additions & 30 deletions core/lib/JsPath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,7 @@ import {
getClientRectFnName,
getComputedVisibilityFnName,
getNodePointerFnName,
runMagicSelectorAllFnName,
runMagicSelectorFnName,
} from '@ulixee/hero-interfaces/jsPathFnNames';
import IMagicSelectorOptions from '@ulixee/hero-interfaces/IMagicSelectorOptions';
import IElementRect from '@ulixee/hero-interfaces/IElementRect';
import IWaitForElementOptions from '@ulixee/hero-interfaces/IWaitForElementOptions';

Expand Down Expand Up @@ -53,7 +50,6 @@ export class JsPath {
}

public exec<T>(jsPath: IJsPath, containerOffset: IPoint): Promise<IExecJsPathResult<T>> {
if (this.isMagicSelectorPath(jsPath)) this.emitMagicSelector(jsPath[0] as any);
return this.runJsPath<T>(`exec`, jsPath, containerOffset);
}

Expand Down Expand Up @@ -109,8 +105,6 @@ export class JsPath {
options: IWaitForElementOptions,
timeoutMillis: number,
): Promise<IExecJsPathResult<INodeVisibility>> {
if (this.isMagicSelectorPath(jsPath)) this.emitMagicSelector(jsPath[0] as any);

return this.runJsPath<INodeVisibility>(
`waitForElement`,
jsPath,
Expand Down Expand Up @@ -280,30 +274,6 @@ export class JsPath {
}
}

private emitMagicSelector(
jsPath: [query: string, selectorOrOptions: string | IMagicSelectorOptions],
): void {
const [query, selectorOrOptions] = jsPath;
let options = (selectorOrOptions as IMagicSelectorOptions) ?? {
querySelectors: [],
minMatchingSelectors: 1,
};
if (selectorOrOptions && typeof selectorOrOptions === 'string') {
options = { minMatchingSelectors: 1, querySelectors: [selectorOrOptions] };
}

const event = query === runMagicSelectorAllFnName ? 'magic-selector-all' : 'magic-selector';
this.frameEnvironment.tab.emit(event, { options, frame: this.frameEnvironment });
jsPath[1] = options;
}

private isMagicSelectorPath(jsPath: IJsPath): boolean {
return (
Array.isArray(jsPath[0]) &&
(jsPath[0][0] === runMagicSelectorFnName || jsPath[0][0] === runMagicSelectorAllFnName)
);
}

private getJsPathMethod(jsPath: IJsPath): string {
const last = jsPath[jsPath.length - 1];
return Array.isArray(last) ? last[0] : '';
Expand Down
3 changes: 0 additions & 3 deletions core/lib/Tab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ import ICollectedFragment from '@ulixee/hero-interfaces/ICollectedFragment';
import ScreenshotsTable from '../models/ScreenshotsTable';
import { IStorageChangesEntry } from '../models/StorageChangesTable';
import { IRemoteEmitFn, IRemoteEventListener } from '../interfaces/IRemoteEventListener';
import IMagicSelectorOptions from '@ulixee/hero-interfaces/IMagicSelectorOptions';
import GlobalPool, { disableMitm } from './GlobalPool';
import IWebsocketMessage from '@ulixee/hero-interfaces/IWebsocketMessage';
import MirrorPage from '@ulixee/hero-timetravel/lib/MirrorPage';
Expand Down Expand Up @@ -1264,8 +1263,6 @@ export interface ITabEventParams {
'resource-requested': IResourceMeta;
resource: IResourceMeta;
'websocket-message': IWebsocketResourceMessage;
'magic-selector': { options: IMagicSelectorOptions; frame: FrameEnvironment };
'magic-selector-all': { options: IMagicSelectorOptions; frame: FrameEnvironment };
}

export function stringToRegex(str: string): RegExp {
Expand Down
16 changes: 0 additions & 16 deletions core/test/interact.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ import ConnectionToClient from '../connections/ConnectionToClient';
import {
getClientRectFnName,
getNodePointerFnName,
runMagicSelectorFnName,
} from '@ulixee/hero-interfaces/jsPathFnNames';
import IMagicSelectorOptions from '@ulixee/hero-interfaces/IMagicSelectorOptions';
import IElementRect from '@ulixee/hero-interfaces/IElementRect';
import { LoadStatus, LocationStatus } from '@ulixee/hero-interfaces/Location';

Expand Down Expand Up @@ -147,20 +145,6 @@ describe('basic interaction tests', () => {
},
]),
).rejects.toThrow('element does not exist');

await expect(
tab.interact([
{
command: InteractionCommand.click,
mousePosition: [
[
runMagicSelectorFnName,
{ querySelectors: ['.not-there'], minMatchingSelectors: 1 } as IMagicSelectorOptions,
],
],
},
]),
).rejects.toThrow('element does not exist');
});

it('moves over a select box to simulate clicking an option', async () => {
Expand Down
24 changes: 5 additions & 19 deletions docs/main/BasicInterfaces/FrameEnvironment.md
Original file line number Diff line number Diff line change
Expand Up @@ -257,32 +257,18 @@ Determines if an element is visible to an end user. This method checks whether a

#### **Returns**: `Promise<boolean>` Whether the element is visible to an end user.

### frameEnvironment.magicSelector*(stringOrOptions)* {#magic-selector}
### frameEnvironment.querySelector*(stringOrOptions)* {#query-selector}

The magic selector is a drop-in replacement for document.querySelector, but it allows a pattern to be provided which can verify your selected elements match multiple queries.

#### **Arguments**:
- stringOrOptions `string` or `object`. Optional. When not provided and using Superhero, the MagicSelector designer is activated.
- `string`. When a string, it will match the given selector string
- `object`. When an object, the following properties are options:
- minMatchingSelectors `number`. The minimum number of selectors in the provided list that must match.
- querySelectors `string[]`. The list of query selectors to match from.
This is a shortcut for document.querySelector.

#### **Returns**: [`SuperNode`](/docs/awaited-dom/super-node). A Node that satisfies the given patterns. Evaluates to null if awaited and not present.


### frameEnvironment.magicSelectorAll*(stringOrOptions)* {#magic-selector-all}
### frameEnvironment.querySelectorAll*(stringOrOptions)* {#query-selector-all}

The magicSelectorAll function is a drop-in replacement for document.querySelectorAll, but it allows a pattern to be provided which can verify your selected elements match multiple queries. For this function, the full returned list of matching nodes must be the same for it to be considered a "match".

#### **Arguments**:
- stringOrOptions `string` or `object`. Optional. When not provided and using Superhero, the MagicSelector designer is activated.
- `string`. When a string, it will match the given selector string
- `object`. When an object, the following properties are options:
- minMatchingSelectors `number`. The minimum number of selectors in the provided list that must match.
- querySelectors `string[]`. The list of query selectors to match from.
This is a shortcut for document.querySelectorAll.

#### **Returns**: [`SuperNodeList`](/docs/awaited-dom/super-node-list). A NodeList that satisfies the given patterns. Returns an empty list if a resultset is not found that satisfies the constraints.
#### **Returns**: [`SuperNodeList`](/docs/awaited-dom/super-node-list). A NodeList that satisfies the given selector. Returns an empty list if a resultset is not found.

### frameEnvironment.waitForPaintingStable*(options)* {#wait-for-painting-stable}

Expand Down
8 changes: 4 additions & 4 deletions docs/main/BasicInterfaces/Hero.md
Original file line number Diff line number Diff line change
Expand Up @@ -533,13 +533,13 @@ Alias for [Tab.goto](/docs/basic-interfaces/tab#goto)

Alias for [Tab.getComputedVisibility](/docs/basic-interfaces/tab#get-computed-visibility)

### hero.magicSelector*(stringOrOptions)* {#magic-selector}
### hero.querySelector*(stringOrOptions)* {#query-selector}

Alias for [Tab.magicSelector](/docs/basic-interfaces/tab#magic-selector)
Alias for [Tab.querySelector](/docs/basic-interfaces/tab#query-selector)

### hero.magicSelectorAll*(stringOrOptions)* {#magic-selector-all}
### hero.querySelectorAll*(stringOrOptions)* {#query-selector-all}

Alias for [Tab.magicSelectorAll](/docs/basic-interfaces/tab#magic-selector-all)
Alias for [Tab.querySelectorAll](/docs/basic-interfaces/tab#query-selector-all)

### hero.reload*(timeoutMs?)* {#reload}

Expand Down
22 changes: 4 additions & 18 deletions docs/main/BasicInterfaces/Tab.md
Original file line number Diff line number Diff line change
Expand Up @@ -280,30 +280,16 @@ Alias for [tab.mainFrameEnvironment.getComputedVisibility](/docs/basic-interface
- hasDimensions `boolean`. The node has width and height.
- isUnobstructedByOtherElements `boolean`. The node is not hidden or obscured > 50% by another element.

### tab.magicSelector*(stringOrOptions)* {#magic-selector}
### tab.querySelector*(stringOrOptions)* {#query-selector}

The magic selector is a drop-in replacement for document.querySelector, but it allows a pattern to be provided which can verify your selected elements match multiple queries.

#### **Arguments**:
- stringOrOptions `string` or `object`. Optional. When not provided and using Superhero, the MagicSelector designer is activated.
- `string`. When a string, it will match the given selector string
- `object`. When an object, the following properties are options:
- minMatchingSelectors `number`. The minimum number of selectors in the provided list that must match.
- querySelectors `string[]`. The list of query selectors to match from.
This is a shortcut for document.querySelector.

#### **Returns**: [`SuperNode`](/docs/awaited-dom/super-node). A Node that satisfies the given patterns. Evaluates to null if awaited and not present.


### tab.magicSelectorAll*(stringOrOptions)* {#magic-selector-all}
### tab.querySelectorAll*(stringOrOptions)* {#query-selector-all}

The magicSelectorAll function is a drop-in replacement for document.querySelectorAll, but it allows a pattern to be provided which can verify your selected elements match multiple queries. For this function, the full returned list of matching nodes must be the same for it to be considered a "match".

#### **Arguments**:
- stringOrOptions `string` or `object`. Optional. When not provided and using Superhero, the MagicSelector designer is activated.
- `string`. When a string, it will match the given selector string
- `object`. When an object, the following properties are options:
- minMatchingSelectors `number`. The minimum number of selectors in the provided list that must match.
- querySelectors `string[]`. The list of query selectors to match from.
This is a shortcut for document.querySelectorAll.

#### **Returns**: [`SuperNodeList`](/docs/awaited-dom/super-node-list). A NodeList that satisfies the given patterns. Returns an empty list if a resultset is not found that satisfies the constraints.

Expand Down
Loading

0 comments on commit 507a310

Please sign in to comment.