Skip to content

Commit 779d2a8

Browse files
author
Jason Byrne
committed
2.0 is now working great, not published yet but almost ready
1 parent 82cb6f3 commit 779d2a8

13 files changed

+702
-530
lines changed

.vscode/launch.json

+6
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@
1818
"request": "launch",
1919
"name": "Example - Browser (Google)",
2020
"program": "${workspaceFolder}/example/browser/google.js",
21+
},
22+
{
23+
"type": "node",
24+
"request": "launch",
25+
"name": "Example - MileSplit",
26+
"program": "${workspaceFolder}/example/milesplit.js",
2127
}
2228
]
2329
}

example/browser/google.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ const paths = {
2020
searchResultsItem: '#search div.g'
2121
}
2222

23-
suite.Browser('Google Search for Flagpole', browserOpts)
23+
suite.browser('Google Search for Flagpole', browserOpts)
2424
.open('/')
25-
.then(async function () {
25+
.next(async function () {
2626
this.assert('Landing Page HTTP Status is 200', this.response.status()).equals(200);
2727
await this.assert('Wait for search input box', this.exists(paths.queryInput)).resolves();
2828
await this.page.type(paths.queryInput, searchTerm);

example/milesplit.js

+11-7
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,25 @@ let suite = Flagpole.Suite('Test MileSplit')
44
.base('https://www.milesplit.com')
55
.finally(() => { suite.print(); });
66

7-
suite.Html('Test MileSplit Hompage').open('/')
8-
.then('Load up homepage and verify response', function () {
7+
suite.html('Test MileSplit Hompage').open('/')
8+
.next('Load up homepage and verify response', function () {
99
this.assert('HTTP Status is 200', this.response.status()).equals(200);
1010
this.assert('Content type is HTML', this.response.headers('content-type')).contains('text/html');
1111
})
12-
.then('Look for images in top stories', async function () {
12+
.next('Look for images in top stories', async function () {
1313
const coverImages = await this.selectAll('section.topStories figure img');
1414
this.assert('There should be at least 4 top stories', coverImages.length)
1515
.greaterThanOrEquals(4);
1616
return coverImages;
1717
})
18-
.then(async function (response, context) {
18+
.next('Verify images load', async function () {
1919
const coverImages = await this.result;
20-
coverImages.forEach(async function (element, i) {
21-
context.comment(await element.getAttribute('src'));
20+
this.assert('Every cover image has src attribute', coverImages).every(async function(image) {
21+
return (await image.hasAttribute('src'));
22+
});
23+
coverImages.forEach(async function (image, i) {
24+
image.load(`Load Cover Image ${i + 1}`, async function () {
25+
this.assert('Width is 620', await this.select('width')).equals(620);
26+
});
2227
});
23-
await this.comment(await (await this.select('.topStories')).getClassName());
2428
});

example/smoke.js

+7-7
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ Flagpole.Suite('Smoke Tests')
55
.finally(function (suite) {
66
suite.print();
77
})
8-
.Html('Homepage').open('/')
9-
.then('Do basic homepage tests', function () {
8+
.html('Homepage').open('/')
9+
.next('Do basic homepage tests', function () {
1010
this.response.status().equals(200)
1111
.headers('content-type').contains('text/html')
1212
.select('title').text().contains('Stack Overflow')
1313
.select('link')
1414
.not().select('frameset');
1515
})
16-
.then('Test the top navigation bar', function () {
16+
.next('Test the top navigation bar', function () {
1717
this.response
1818
.label('Top bar and call to actions exists')
1919
.select('.top-bar').find('.-ctas').length().greaterThan(0)
@@ -24,7 +24,7 @@ Flagpole.Suite('Smoke Tests')
2424
.select('a.login-link').nth(1)
2525
.and().text().similarTo('Sign Up');
2626
})
27-
.then('There should be questions', function () {
27+
.next('There should be questions', function () {
2828
this.response
2929
.select('.question-summary')
3030
.and().length().greaterThan(5)
@@ -35,17 +35,17 @@ Flagpole.Suite('Smoke Tests')
3535
.and().find('.status span').exists()
3636
.and().text().parseInt().greaterThanOrEquals(0);
3737
})
38-
.then('Test that each image exists', function () {
38+
.next('Test that each image exists', function () {
3939
this.response.select('img').each(function (img, index) {
4040
img.load('Image ' + index, true);
4141
});
4242
})
43-
.then('Test that the stylesheets exist', function () {
43+
.next('Test that the stylesheets exist', function () {
4444
this.response.select('link[rel="stylesheet"]').each(function (link, index) {
4545
link.load('Stylesheet ' + index, true);
4646
});
4747
})
48-
.then('Test that the javascript files exist', function () {
48+
.next('Test that the javascript files exist', function () {
4949
this.response.select('script[src]').each(function (script, index) {
5050
script.load('Script ' + index, true);
5151
})

src/assertion.ts

+36-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { AssertionContext } from './assertioncontext';
22
import { Value } from './value';
3-
import { Flagpole } from '.';
43

54
export class Assertion {
65

@@ -225,4 +224,40 @@ export class Assertion {
225224
return this;
226225
}
227226

227+
public none(callback: Function): boolean {
228+
let bool: boolean = false;
229+
if (this._assertValue.isArray()) {
230+
const arr: Array<any> = this._assertValue.get();
231+
bool = arr.every((value: any, index: number, array: any[]) => {
232+
return !callback(value, index, array);
233+
});
234+
}
235+
this._assert(bool, 'None');
236+
return bool;
237+
}
238+
239+
public every(callback: Function): boolean {
240+
let bool: boolean = false;
241+
if (this._assertValue.isArray()) {
242+
const arr: Array<any> = this._assertValue.get();
243+
bool = arr.every((value: any, index: number, array: any[]) => {
244+
return callback(value, index, array);
245+
});
246+
}
247+
this._assert(bool, 'Every');
248+
return bool;
249+
}
250+
251+
public some(callback: Function): boolean {
252+
let bool: boolean = false;
253+
if (this._assertValue.isArray()) {
254+
const arr: Array<any> = this._assertValue.get();
255+
bool = arr.some((value: any, index: number, array: any[]) => {
256+
return callback(value, index, array);
257+
});
258+
}
259+
this._assert(bool, 'Some');
260+
return bool;
261+
}
262+
228263
}

src/assertioncontext.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export class AssertionContext {
1212
private _response: iResponse;
1313

1414
private get _isBrowserRequest(): boolean {
15-
return this._scenario.getResponseType() == ResponseType.browser;
15+
return this._scenario.responseType == ResponseType.browser;
1616
}
1717

1818
public result: any;

src/flagpole.ts

+1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export class Flagpole {
5454
* @returns {Suite}
5555
* @constructor
5656
*/
57+
static suite = Flagpole.Suite;
5758
static Suite(title: string): Suite {
5859
let suite: Suite = new Suite(title);
5960
return suite;

src/link.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,13 @@ export class Link {
7373
return /^(ftp):/.test(this.uri);
7474
}
7575

76+
/*
7677
public isNonNavigation(): boolean {
77-
return /^(gopher|archie|veronica|telnet|file|nntp|news|irc|spdy|rtmp|rtp|tcp|udp):\/\//i.test(this.uri);
78+
return (
79+
/^(gopher|archie|veronica|telnet|file|nntp|news|irc|spdy|rtmp|rtp|tcp|udp):\/\//i.test(this.uri)
80+
);
7881
}
82+
*/
7983

8084
public isNavigation(): boolean {
8185
return (

src/nodeelement.ts

+86-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
import { ProtoValue } from './value';
22
import { JSHandle } from 'puppeteer';
33
import { Flagpole } from '.';
4+
import { Link } from './link';
5+
import { Scenario } from './scenario';
6+
import { ResponseType, iResponse } from './response';
47

58
export class NodeElement extends ProtoValue {
69

710
public async isFormTag(): Promise<boolean> {
811
if (this.isCheerioElement() || this.isPuppeteerElement()) {
9-
return await this.getTagName() === 'form';
12+
return (await this.getTagName()) == 'form';
1013
}
1114
return false;
1215
}
@@ -154,6 +157,10 @@ export class NodeElement extends ProtoValue {
154157
return null;
155158
}
156159

160+
public async hasAttribute(key: string): Promise<boolean> {
161+
return (await this.getAttribute(key)) !== null;
162+
}
163+
157164
public async getAttribute(key: string): Promise<any> {
158165
if (this.isCheerioElement()) {
159166
return (typeof this._input.get(0).attribs[key] !== 'undefined') ?
@@ -184,6 +191,10 @@ export class NodeElement extends ProtoValue {
184191
return null;
185192
}
186193

194+
public async hasData(key: string): Promise<boolean> {
195+
return (await this.getData(key)) !== null;
196+
}
197+
187198
public async getData(key: string): Promise<any> {
188199
let text: any;
189200
if (this.isCheerioElement()) {
@@ -290,4 +301,78 @@ export class NodeElement extends ProtoValue {
290301
return null;
291302
}
292303

304+
/**
305+
* Load the URL from this NodeElement if it has something to load
306+
* This is used to create a lambda scenario
307+
*
308+
* @param a
309+
*/
310+
public async load(a?: string | Function, b?: Function): Promise<Scenario> {
311+
const element: NodeElement = this;
312+
const srcPath: string | null = await element.getUrl();
313+
const link: Link = new Link(element._context.response, srcPath || '');
314+
const title: string = typeof a == 'string' ? a : `Load ${srcPath}`;
315+
const callback: Function = (function () {
316+
// Handle overloading
317+
if (typeof b == 'function') {
318+
return b;
319+
}
320+
else if (typeof a == 'function') {
321+
return a;
322+
}
323+
// No callback was set, so just create a blank one
324+
else {
325+
return function () { };
326+
}
327+
})();
328+
const scenario: Scenario = element._context.suite.Scenario(title);
329+
// Is this link one that we can actually load?
330+
if (link.isNavigation()) {
331+
const scenarioType: string = await element.getLambdaScenarioType();
332+
const uri: string = link.getUri();
333+
// Get the options or just pass the default
334+
const opts: any = (
335+
(scenarioType == 'browser' && element._context.scenario.responseType == ResponseType.browser) ||
336+
scenarioType != 'browser'
337+
) ? element._context.scenario.getRequestOptions() : {};
338+
// Initialize the scenario
339+
scenario[scenarioType](opts);
340+
// Set our callback
341+
scenario.next(callback);
342+
// Done. Execute it asynchronously
343+
setTimeout(() => {
344+
scenario.open(uri);
345+
}, 1);
346+
}
347+
else {
348+
scenario.skip('Not a navigational link');
349+
}
350+
return scenario;
351+
}
352+
353+
public async getLambdaScenarioType(): Promise<string> {
354+
if (
355+
(await this.isFormTag()) || (await this.isClickable())
356+
) {
357+
// Assume if we are already in browser mode, we want to stay there
358+
return (this._context.scenario.responseType == ResponseType.browser) ?
359+
'browser' : 'html';
360+
}
361+
else if (await this.isImageTag()) {
362+
return 'image';
363+
}
364+
else if (await this.isStylesheetTag()) {
365+
return 'stylesheet';
366+
}
367+
else if (await this.isScriptTag()) {
368+
return 'script';
369+
}
370+
else if (await this.isVideoTag()) {
371+
return 'video'
372+
}
373+
else {
374+
return 'resource';
375+
}
376+
}
377+
293378
}

src/response.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ export abstract class GenericResponse implements iResponse {
292292
* Load time of request to response
293293
*/
294294
public loadTime(): Node {
295-
return new Node(this, 'Load Time', this.scenario.getRequestLoadTime());
295+
return new Node(this, 'Load Time', this.scenario.requestDuration);
296296
}
297297

298298
/**

src/responsefactory.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { Scenario } from './scenario';
1010
import { NormalizedResponse, iResponse, ResponseType } from './response';
1111

1212
export function createResponse(scenario: Scenario, response: NormalizedResponse): iResponse {
13-
const type: ResponseType = scenario.getResponseType();
13+
const type: ResponseType = scenario.responseType;
1414
let className;
1515
if (type == ResponseType.html) {
1616
className = HtmlResponse;

0 commit comments

Comments
 (0)