

import xlib = require( "xlib" );
import phantomjscloud = require( "./_index" );
import ioDatatypes = phantomjscloud.ioDatatypes;
const __ = xlib.lolo;
const log = __.log;
const _ = xlib.lodash;



const __verifyResponseStatus_defaultOptions = { contentStatusCode: 200, userResponseStatusCode: 200, backend: "chrome", doneDetail: "normal", contentType: "" };
/** does basic verification of the userResponse common among most tests */
function verifyResponseStatus( userResponse: ioDatatypes.IUserResponse, options: Partial<typeof __verifyResponseStatus_defaultOptions> = __verifyResponseStatus_defaultOptions ) {

	options = xlib.lodash.defaultsDeep( options, __verifyResponseStatus_defaultOptions );
	const responseSummary = xlib.serialization.jsonX.inspectParse( userResponse );

	log.assert( userResponse != null, "response null", { options, userResponse } );
	log.assert( userResponse.statusCode === options.userResponseStatusCode, "responseStatusCode", userResponse.statusCode, { options, responseSummary } );
	log.assert( userResponse.content.statusCode === options.contentStatusCode, "contentStatusCode", userResponse.content.statusCode, { options, responseSummary } );
	log.assert( userResponse.meta.backend.platform.toLowerCase() === options.backend.toLowerCase(), "backend", userResponse.meta.backend, { options, responseSummary } );
	if ( options.backend === "chrome" ) {
		log.assert( JSON.stringify( userResponse.content.doneDetail ).includes( options.doneDetail ), "doneDetail", userResponse.content.doneDetail, { options, responseSummary } );
	}
	if ( options.contentType != null && options.contentType.length > 0 ) {
		log.assert( userResponse.content.headers[ "content-type" ].includes( options.contentType ), `content-type header not what expected. got ${ userResponse.content.headers[ "content-type" ] } but expect it to include "${ options.contentType }"` );
	}

	log.assert( userResponse.meta.billing.creditCost > 0, "invalid creditCost" );
	log.assert( userResponse.meta.billing.dailySubscriptionCreditsRemaining >= 0, "invalid dailySubscriptionCreditsRemaining" );
	log.assert( userResponse.meta.billing.prepaidCreditsRemaining >= 0, "invalid prepaidCreditsRemaining" );

	xlib.lodash.forEach( userResponse.pageResponses, ( pageResponse ) => {
		//log.assert( pageResponse.metrics.pageStatus === options.contentStatusCode.toString(), "pageResponse.metrics.pageStatus", pageResponse.metrics.pageStatus, { options, pageResponse: JSON.stringify( pageResponse ) } );
		if ( options.backend === "chrome" ) {
			log.assert( JSON.stringify( pageResponse.doneDetail ).includes( options.doneDetail ), "pageResponse.doneDetail", pageResponse.doneDetail, { options, pageResponse: JSON.stringify( pageResponse ) } );
		}
	} );
}
/** helper to set all properties to null */
function _nullAllProperties( obj: any ) {
	_.forEach( obj, ( value, key ) => {
		if ( _.isObject( value ) ) {
			_nullAllProperties( value );
			obj[ key ] = value;
		} else {
			obj[ key ] = null;
		}
	} );
}

/** hack fix for mocha bug, unable to have a timeout for async tests */
function it2( testFcn: () => Promise<any> ) {
	const testName = xlib.reflection.getTypeName( testFcn );
	return it( testName, async function () {
		const timeoutMs = this.timeout();
		return xlib.promise.bluebird.resolve( testFcn.apply( this ) ).timeout( timeoutMs, new xlib.promise.bluebird.TimeoutError( `operation timed out.  Max of ${ timeoutMs }ms exceeded` ) );
	} );
}

describe( __filename, function unitTests() {

	let browser: phantomjscloud.BrowserApi;

	this.timeout( 10000 ); //set default timeout for these pjsc tests
	this.beforeAll( function beforeAll() {

		const apiKey = xlib.environment.getEnvironmentVariable( "phantomjscloud_apikey" );
		const endpointOrigin: string =
			//	"http://localhost:80"; //used for UAT testing
			//"https://phantomjscloud.com"; //PROD (explicit)
			//"http://35.232.215.225"; //PREPROD
			undefined; //PROD (implicit, default)

		browser = new phantomjscloud.BrowserApi( { apiKey, endpointOrigin } );


	} );




	describe( "e2eDefaultBrowser", function e2eDefaultBrowser() {

		let test = it2( async function basicE2e() {

			const pageRequest: phantomjscloud.ioDatatypes.IPageRequest = {
				url: "https://localhost/examples/corpus/example.com.html", renderType: "html", backend: "chrome"
			};

			const userResponse = await browser.requestSingle( pageRequest );
			log.assert( userResponse.content.data.indexOf( "Example Domain" ) >= 0 );

		} );
		test.timeout( 20000 );


		it2( async function htmlBasicPageRequest() {

			const pageRequest: ioDatatypes.IPageRequest = {
				url: "http://localhost/examples/corpus/example.com.html",
				renderType: "plainText",
			};

			const response = await browser.requestSingle( pageRequest );
			verifyResponseStatus( response );
			log.assert( response.content.data.includes( "Example Domain" ), "content verification failed", response.content.data );

		} );

		// ! production servers fulfill this too fast, so even 50ms ajax calls finish too rapidly for it to be waited on.
		// // it2( async function gracefulFailureExceedMaxWait() {
		// // 	const pageRequest: ioDatatypes.IPageRequest = {
		// // 		url: "http://localhost/examples/corpus/ajax-fast.html",
		// // 		renderType: "plainText",
		// // 		requestSettings: {
		// // 			maxWait: 3000,
		// // 		}
		// // 	};
		// // 	const response = await browser.requestSingle( pageRequest );
		// // 	verifyResponseStatus( response, { contentStatusCode: 408, doneDetail: "error:maxWait" } );
		// // 	log.assert( response.content.data.includes( "this page will make ajax calls" ), "content verification failed", response.content.data );
		// // } );



		it2( async function contentInjection() {
			const pageRequest: ioDatatypes.IPageRequest = {
				url: "http://localhost/examples/helpers/requestdata",
				renderType: "plainText",
				content: "<html><h1>hello-world</h1></html>",
			};
			const response = await browser.requestSingle( pageRequest );
			verifyResponseStatus( response );
			log.assert( response.content.data.includes( "hello-world" ), "content verification failed", response.content.data );
		} );

		it2( async function redirectJs() {
			const pageRequest: ioDatatypes.IPageRequest = {
				url: "http://localhost/examples/helpers/requestdata",
				renderType: "plainText",
				content: "<html><script>window.location='https://localhost/examples/corpus/example.com.html';</script><h1>hello-world</h1></html>",
			};
			const response = await browser.requestSingle( pageRequest );
			verifyResponseStatus( response );
			log.assert( response.content.data.includes( "This domain is established to be used" ), "content verification failed", response.content.data );
		} );


		it2( async function doneWhenSelector() {
			const pageRequest: ioDatatypes.IPageRequest = {
				url: "http://localhost/examples/corpus/ajax.html",
				renderType: "plainText",
				requestSettings: {
					doneWhen: [ { text: `"statusCode":206`, statusCode: 202 }, { selector: "pre#fill-target", statusCode: 201 } ],
				},
			};
			const response = await browser.requestSingle( pageRequest );
			verifyResponseStatus( response, { contentStatusCode: 201, doneDetail: "pre#fill-target" } );
			log.assert( response.content.data.includes( "this page will make ajax calls" ), "content verification failed", response.content.data );
		} );

		it2( async function doneWhenDomReady() {
			// loads up an expensive page and renders as soon as possible, tossing out unneeded contents

			const pageRequest: ioDatatypes.IPageRequest = {
				url: "http://cnn.com",
				renderType: "plainText",
				requestSettings: {
					doneWhen: [ { event: "domReady" } ],
					ignoreImages: true,
					waitInterval: 0,
					resourceWait: 100,
					//disableJavascript:true,
					resourceModifier: [
						{ isBlacklisted: true, regex: ".*" },
						{ isBlacklisted: false, regex: ".*cnn.*" },
						{ isBlacklisted: true, category: "subFrameResource" },
						{ isBlacklisted: true, type: "stylesheet" },
						{ isBlacklisted: true, type: "image" },
						{ isBlacklisted: true, type: "media" },
						{ isBlacklisted: true, type: "texttrack" },
						{ isBlacklisted: true, type: "websocket" },
						{ isBlacklisted: true, type: "other" },
						{ isBlacklisted: true, type: "eventsource" },
						{ isBlacklisted: true, type: "fetch" },
						{ isBlacklisted: true, type: "font" },
						{ isBlacklisted: true, type: "manifest" },
						//{ isBlacklisted: true, type: "xhr" } ,
					],
				}
			};
			const response = await browser.requestSingle( pageRequest );
			verifyResponseStatus( response, { doneDetail: "domReady" } );
			log.assert( response.content.data.includes( "Politics" ), "content verification failed", response.content.data );
		} ).timeout( 15000 );


		// ! prod is too fast for the ajax to delay rendering.   so this forceFinish() test doesn't work.
		// it2( async function forceFinish() {
		// 	const pageRequest: ioDatatypes.IPageRequest = {
		// 		url: "http://localhost/examples/corpus/ajax-fast.html",
		// 		renderType: "plainText",
		// 		requestSettings: { waitInterval: 500 },
		// 		renderSettings: {
		// 			viewport: { height: 20, width: 200 },
		// 		},
		// 		scripts: { domReady: [ "setInterval(function(){var lastY=window.scrollY;window.scrollBy(0,20);if(lastY===window.scrollY){window._pjscMeta.forceFinish=true;}},50);" ] },
		// 	};
		// 	const response = await browser.requestSingle( pageRequest );
		// 	verifyResponseStatus( response, { contentStatusCode: 200, doneDetail: "forceFinish" } );
		// 	log.assert( response.content.data.includes( "this page will make ajax calls" ), "content verification failed", response.content.data );
		// } );

		it2( async function htmlBasicUserRequest() {

			const userRequest: ioDatatypes.IUserRequest = {
				pages: [ {
					url: "http://localhost/examples/helpers/requestdata",
					renderType: "plainText",
					requestSettings: {
						maxWait: 5000,
					}
				} ],
			};
			const response = await browser.requestSingle( userRequest );
			verifyResponseStatus( response );
			log.assert( response.content.data.includes( `"user-agent": "Mozilla` ), "content verification failed", response.content.data );
		} );


		it2( async function resourceModifier_changeUrl() {

			const pageRequest: ioDatatypes.IPageRequest = {
				url: "http://www.highcharts.com/demo/pie-donut",
				renderType: "html", requestSettings: {
					resourceModifier: [ { regex: ".*highcharts.com.*", changeUrl: "$$protocol:$$port//en.wikipedia.org/wiki$$path" } ]
				}
			};
			const response = await browser.requestSingle( pageRequest );
			verifyResponseStatus( response, { contentStatusCode: 404 } );
			log.assert( response.content.data.includes( "<title>Demo/pie-donut - Wikipedia</title>" ), "content verification failed", response.content.data );
		} );


		it2( async function cookies_inject() {

			const pageRequest: ioDatatypes.IPageRequest = {
				url: "http://localhost/examples/corpus/example.com.html",
				renderType: "plainText",
				requestSettings: {
					cookies: [
						{ domain: "example.com", name: "myCookie1", value: "value1" },
						{ domain: "localhost", name: "myCookie2", value: "value2" } ]
				}
			};
			const response = await browser.requestSingle( pageRequest );
			verifyResponseStatus( response );
			log.assert( response.pageResponses[ 0 ].cookies.length === 1, "cookie verification failed: cookie count", response.pageResponses[ 0 ].cookies.length );
			log.assert( response.pageResponses[ 0 ].cookies[ 0 ].name === "myCookie2", "cookie verification failed: name" );
			log.assert( response.pageResponses[ 0 ].cookies[ 0 ].value === "value2", "cookie verification failed: value" );
			log.assert( response.content.data.includes( "Example Domain" ), "content verification failed", response.content.data );
		} );


		it2( async function cors_bypass_and_loadFinished_script() {

			const pageRequest: ioDatatypes.IPageRequest = {
				url: "https://news.ycombinator.com/",
				renderType: "html",
				backend: "chrome",
				requestSettings: {
					maxWait: 10000,
					disableSecureHeaders: true,
				},
				scripts: {
					domReady: [ "http://localhost/examples/scripts/hilitor.js" ],
					loadFinished: [ "var myHilitor = new Hilitor(); myHilitor.apply('ask,who,jobs,new');" ]
				}
			};
			const response = await browser.requestSingle( pageRequest );
			verifyResponseStatus( response );
			log.assert( response.content.data.includes( `<em class="hilitor"` ), "content verification failed", response.content.data );
		} );



		it2( async function translation() {

			const pageRequest: ioDatatypes.IPageRequest = {
				url: "http://localhost/examples/corpus/example.com.html",
				renderType: "plainText",
				requestSettings: { disableSecureHeaders: true, },
				scripts: { pageNavigated: [ "http://localhost/examples/scripts/google-translate.js?targetLang=ko&hideUi=true" ] }
			};
			const response = await browser.requestSingle( pageRequest );
			verifyResponseStatus( response );
			log.assert( response.content.data.indexOf( `예 도메인` ) === 0, "content verification failed", response.content.data );
		} );


		it2( async function redirect_with_scripts_also_verify_content_properties() {
			const pageRequest: ioDatatypes.IPageRequest = {
				url: "http://localhost/examples/corpus/redirect.html",
				renderType: "html",
				scripts: {
					domReady: [ "http://localhost/examples/scripts/hilitor.js" ],
					loadFinished: [ "var myHilitor = new Hilitor(); myHilitor.apply('simple,page,example,http');" ]
				}
			};
			const response = await browser.requestSingle( pageRequest );
			verifyResponseStatus( response );
			log.assert( response.content.url === "http://localhost/examples/corpus/simple.html", "should have reddirected" );
			log.assert( response.content.data.includes( `<em class="hilitor" style="background-color: rgb(255, 255, 102); font-style: inherit; color: rgb(0, 0, 0);">Simple</em> Html` ), "content verification failed, scripts did not exec properly", response.content.data );
			log.assert( response.content.name === "localhost-examples-corpus-simple.html", "content name not set properly" );
			log.assert( response.content.pageExecLastWaitedOn.includes( "waitInterval" ), "expected waitInterval to be last thing our page wated on finishing" );
			log.assert( response.content.encoding === "utf8", "expect utf8 encoding for our html output" );

		} );



		it2( async function postBasic() {

			const pageRequest: ioDatatypes.IPageRequest = {
				url: "http://localhost/examples/helpers/requestdata",
				renderType: "html",
				urlSettings: {
					data: "my=postData",
					operation: "POST",
				},
			};
			const response = await browser.requestSingle( pageRequest );
			verifyResponseStatus( response );
			log.assert( response.content.data.includes( `"my": "postData"` ), "content verification failed, post payload not found", response.content.data );
		} );

		it2( async function postJson_with_passThroughHeaders() {

			const pageRequest: ioDatatypes.IPageRequest = {
				url: "http://localhost/examples/helpers/requestdata",
				renderType: "plainText",
				outputAsJson: true,
				urlSettings: {
					operation: "POST",
					headers: { hello: "world", "Content-Type": "application/json" },
					data: "{\"yourPostData\":123}",
				},
				renderSettings: {
					passThroughHeaders: true,
				}
			};
			const response = await browser.requestSingle( pageRequest );
			verifyResponseStatus( response, { contentType: "application/json" } );
			log.assert( response.content.data.includes( `"yourPostData": 123` ), "content verification failed, post payload not found", response.content.data );
		} );

		it2( async function postUnicode() {

			const pageRequest: ioDatatypes.IPageRequest = {
				url: "http://localhost/examples/helpers/requestdata",
				renderType: "plainText",
				outputAsJson: false,
				urlSettings: {
					operation: "POST",
					// tslint:disable-next-line: max-line-length
					data: "{\"loans\":[{\"account_id\":\"49790094591\",\"account_name\":\"สินเชื่อรถยนต์ใหม่\",\"account_type\":\"CARLOAN\",\"customer_name\":\"นาย บีเอสซีบีคอนเนค คอนเนค นาย\",\"status\":\"ACTIVE\",\"scb_connect\":true,\"linked_date\":\"1537499724000\"},{\"account_id\":\"49790094606\",\"account_name\":\"สินเชื่อรถยนต์ใหม่\",\"account_type\":\"CARLOAN\",\"customer_name\":\"นาย บีเอสซีบีคอนเนค คอนเนค นาย\",\"status\":\"INACTIVE\",\"scb_connect\":true,\"linked_date\":\"1537499724000\"},{\"account_id\":\"49790094614\",\"account_name\":\"สินเชื่อรถยนต์ใหม่\",\"account_type\":\"CARLOAN\",\"customer_name\":\"นาย บีเอสซีบีคอนเนค คอนเนค นาย\",\"status\":\"ACTIVE\",\"scb_connect\":true,\"linked_date\":\"1537499724000\"},{\"account_id\":\"49790094648\",\"account_name\":\"สินเชื่อรถยนต์ใหม่\",\"account_type\":\"CARLOAN\",\"customer_name\":\"นาย บีเอสซีบีคอนเนค คอนเนค นาย\",\"status\":\"ACTIVE\",\"scb_connect\":true,\"linked_date\":\"1537499724000\"},{\"account_id\":\"49790094656\",\"account_name\":\"สินเชื่อรถยนต์ใหม่\",\"account_type\":\"CARLOAN\",\"customer_name\":\"นาย บีเอสซีบีคอนเนค คอนเนค นาย\",\"status\":\"ACTIVE\",\"scb_connect\":true,\"linked_date\":\"1537499724000\"}]}"
				},
			};
			const response = await browser.requestSingle( pageRequest );
			verifyResponseStatus( response );
			log.assert( response.content.data.includes( `สินเชื่อรถยนต` ), "content verification failed, post payload not found", response.content.data );
		} );


		it2( async function scriptOutputDomElement() {

			const pageRequest: ioDatatypes.IPageRequest = {
				outputAsJson: false,
				url: "http://localhost/examples/corpus/example.com.html",
				renderType: "script",
				scripts: {
					domReady: [ "http://localhost/examples/scripts/jquery-3.3.1.min.js" ],
					loadFinished: [ "window._pjscMeta.scriptOutput={h1:$('h1').text()};" ]
				}
			};
			const response = await browser.requestSingle( pageRequest );
			verifyResponseStatus( response );

			log.assert( _.isPlainObject( response.content.data ), "content verification failed, script output not as expected.  expect a POJO", response.content.data );
			const scriptOutput: { h1: string; } = response.content.data as any;
			log.assert( scriptOutput.h1 === "Example Domain", "content verification failed, script output h1 element not as expected.  expect contents 'Example Domain'", response.content.data );
			log.assert( response.content.name === "localhost-examples-corpus-example-com.json", "expect file extension to be JSON" );
		} );

		it2( async function selectorTest() {
			const pageRequest: ioDatatypes.IPageRequest = {
				url: "http://localhost/examples/corpus/news.ycombinator.com.html",
				renderType: "plainText",
				renderSettings: {
					selector: ".itemlist",
				},
			};
			const response = await browser.requestSingle( pageRequest );
			verifyResponseStatus( response );
			log.assert( response.content.data.indexOf( "1." ) === 0, "content verification failed, expect first post 'index number' in beginning of content", response.content.data );
		} );



		it2( async function nullOptionalParameters() {


			const pageRequest = ioDatatypes.pageRequestDefaultsGet();
			_nullAllProperties( pageRequest );

			pageRequest.url = "http://localhost/examples/corpus/example.com.html";
			const response = await browser.requestSingle( pageRequest );
			verifyResponseStatus( response );
			log.assert( response.content.encoding === "base64", `content verification failed, expect base64 image.  instead got ${ response.content.encoding }` );
			log.assert( response.content.name === "localhost-examples-corpus-example-com.jpeg", `content verification failed, expect jpeg name.  instead got ${ response.content.name }` );
			log.assert( response.content.size > 30000 && response.content.size < 60000, `content verification failed, expect about 40880 bytes.  instead got ${ response.content.size }` );
		} );

	} ); //end describe("e2e tests");


	describe( "user scenarios", function userScenarios() {

		/** suggested output of the pdfMake tests */
		const pdfMakeOutputStart = "JVBERi0xLjMKJf////8KNSAwIG9iago8PAovVHlwZSAvUGFnZQ";
		const pdfMakeOutputEnd = "Jvb3QgMiAwIFIKL0luZm8gNyAwIFIKPj4Kc3RhcnR4cmVmCjMwMzAKJSVFT0YK";
		/** from email conversation with sean.colonello on 20181017, made script execution synchronous by default, to make this work same as webkit */
		it2( async function chromePdfMake() {
			const pageRequest: ioDatatypes.IPageRequest = {
				url: null,
				backend: "chrome",
				outputAsJson: true,
				content: "",
				requestSettings: {
					maxWait: 6000,
				},
				renderType: "script",
				scripts: {
					domReady: [],
					load: [],
					loadFinished: [ "http://localhost/examples/scripts/pdfmake-0.1.38.min.js", "http://localhost/examples/scripts/pdfmake-0.1.38.vfs_fonts.js", "_pjscMeta.manualWait=true; var dd={content:'Test'};var pdf=pdfMake.createPdf(dd);pdf.getBase64(function(enc){_pjscMeta.scriptOutput=enc;_pjscMeta.manualWait=false;});" ],
				}
			};
			const response = await browser.requestSingle( pageRequest );
			verifyResponseStatus( response );
			log.assert( response.content.name === "localhost-blank.json" );
			log.assert( response.content.size === 4466 );
			log.assert( response.content.data.indexOf( pdfMakeOutputStart ) === 0 );
			log.assert( response.content.data.indexOf( pdfMakeOutputEnd ) === response.content.data.length - pdfMakeOutputEnd.length );
			log.assert( response.content.resourceSummary.complete === 3 );
			log.assert( response.content.resourceSummary.failed === 0 );
			log.assert( response.content.execErrors == null );
		} ).timeout( 6000 );
		/** from email conversation with sean.colonello on 20181017, 
		 * made script execution synchronous by default, this should thus fail because the scripts execute asynchronously
		 * this request is identical to the one above in ```chromePdfMake()``` but this one specifies ```scriptSettings.async=true``` and thus should fail due to race conditions
		 * */
		it2( async function chromePdfMakeFailDueToAsync() {
			const pageRequest: ioDatatypes.IPageRequest = {
				url: null,
				backend: "chrome",
				outputAsJson: true,
				content: "",
				requestSettings: {
					maxWait: 6000,
				},
				renderType: "script",
				scripts: {
					domReady: [],
					load: [],
					loadFinished: [ "http://localhost/examples/scripts/pdfmake-0.1.38.min.js", "http://localhost/examples/scripts/pdfmake-0.1.38.vfs_fonts.js", "_pjscMeta.manualWait=true; var dd={content:'Test'};var pdf=pdfMake.createPdf(dd);pdf.getBase64(function(enc){_pjscMeta.scriptOutput=enc;_pjscMeta.manualWait=false;});" ],
				},
				scriptSettings: {
					async: true,
				},
			};
			const response = await browser.requestSingle( pageRequest );
			verifyResponseStatus( response, { contentStatusCode: 424, doneDetail: "error:maxWait" } );
			log.assert( response.content.name === "localhost-blank.json" );
			log.assert( response.content.size === 2 );
			log.assert( _.isPlainObject( response.content.data ) );
			log.assert( _.isEmpty( response.content.data ) );
			log.assert( response.content.resourceSummary.complete === 3 );
			log.assert( response.content.resourceSummary.failed === 0 );
			log.assert( response.content.execErrors.length === 2 );
			log.assert( _.find( response.content.execErrors, ( execErr ) => execErr.message.includes( "pdfMake is not defined" ) ) != null, "could not find expected error message in content.execErrors" );
		} ).timeout( 10000 );

		/** from email conversation with sean.colonello on 20181017, this worked in webkit but not chrome */
		it2( async function webkitPdfMake() {
			const pageRequest: ioDatatypes.IPageRequest = {
				url: null,
				backend: "webkit",
				outputAsJson: true,
				content: "",
				renderType: "script",
				scripts: {
					domReady: [],
					load: [],
					loadFinished: [ "http://localhost/examples/scripts/pdfmake-0.1.38.min.js", "http://localhost/examples/scripts/pdfmake-0.1.38.vfs_fonts.js",
						"_pjscMeta.manualWait=true; var dd={content:'Test'};var pdf=pdfMake.createPdf(dd);pdf.getBase64(function(enc){_pjscMeta.scriptOutput=enc;_pjscMeta.manualWait=false;});" ],
				}
			};

			const response = await browser.requestSingle( pageRequest );
			verifyResponseStatus( response, { backend: "phantomjs" } );
			log.assert( response.content.name === "content.json" );
			log.assert( response.content.size === 4466 );
			log.assert( response.content.data.indexOf( pdfMakeOutputStart ) === 1 );
			log.assert( response.content.data.indexOf( pdfMakeOutputEnd ) === response.content.data.length - 1 - pdfMakeOutputEnd.length );
		} );

		/** from email conversation with priceline around 20181017, they need to know when a page fails to load properly  */
		it2( async function pricelineBlankPageDetectableError() {
			const pageRequest: ioDatatypes.IPageRequest = {
				url: null,
				content: "http://localhost/examples/customer-tests/priceline-blank.html",
				"outputAsJson": true,
				"requestSettings": {
					"disableJavascript": true,
					"resourceWait": 500,
					"waitInterval": 500,
					//"resourceModifier": [   {    "regex": ".*qaa.priceline.com.*",    "changeUrl": "$$protocol://www.priceline.com$$path",   }  ],
				},
				"renderSettings": {
					"pdfOptions": {
						"format": "onepage"
					}
				},
				"renderType": "plainText",
				//"renderType": "jpeg",
			};
			const response = await browser.requestSingle( pageRequest );
			verifyResponseStatus( response, { contentStatusCode: 408 } ); //prod times out on this 
			log.assert( response.content.name === "localhost-blank.text" );
			log.assert( response.content.data.includes( "Complete Your Booking" ) );//"Hotels Cars Flights" ) );
			log.assert( response.content.resourceSummary.failed > 0 );
		} ).timeout( 40000 );
		/** from email conversation with priceline around 20181017, they need to know when a page fails to load properly  */
		it2( async function pricelineBlankPageDetectableErrorViaTimeout() {
			const pageRequest: ioDatatypes.IPageRequest = {
				url: null,
				content: "http://localhost/examples/customer-tests/priceline-blank.html",
				"outputAsJson": true,
				"requestSettings": {
					"disableJavascript": true,
					"resourceWait": 500,
					"waitInterval": 500,
					maxWait: 10000
					//"resourceModifier": [   {    "regex": ".*qaa.priceline.com.*",    "changeUrl": "$$protocol://www.priceline.com$$path",   }  ],
				},
				"renderSettings": {
					"pdfOptions": {
						"format": "onepage"
					}
				},
				"renderType": "plainText",
				//"renderType": "jpeg",
			};
			const response = await browser.requestSingle( pageRequest );
			verifyResponseStatus( response, { contentStatusCode: 408, doneDetail: "error:maxWait" } );
			log.assert( response.content.name === "localhost-blank.text" );
			log.assert( response.content.data.includes( "Complete Your Booking" ) );//"Hotels Cars Flights" ) );
			log.assert( response.content.resourceSummary.late > 0 );
		} ).timeout( 40000 );

		/** from email conversation with priceline around 20181017, they need to know when a page fails to load properly  */
		it2( async function pricelineBlankPageFix() {
			const pageRequest: ioDatatypes.IPageRequest = {
				url: null,
				content: "http://localhost/examples/customer-tests/priceline-blank.html",
				"outputAsJson": true,
				"requestSettings": {
					"disableJavascript": true,
					"resourceWait": 500,
					"waitInterval": 500,
					"resourceModifier": [ { "regex": ".*qaa.priceline.com.*", "changeUrl": "$$protocol://www.priceline.com$$path", } ],
				},
				"renderSettings": {
					"pdfOptions": {
						"format": "onepage"
					}
				},
				"renderType": "plainText",
				//"renderType": "jpeg",
			};
			const response = await browser.requestSingle( pageRequest );
			verifyResponseStatus( response );
			log.assert( response.content.name === "localhost-blank.text" );
			log.assert( response.content.data.includes( "Complete Your Booking" ) );
			log.assert( response.content.resourceSummary.failed === 0 );
		} ).timeout( 10000 );


	} ); //end describe userScenarios



	describe( "failureTests", function failureTests() {

		it2( async function invalidDomain() {

			let pageRequest: ioDatatypes.IPageRequest = {
				url: "https://www.exadsfakjalkjghlalkjrtiuibe.com",
				renderType: "plainText",
			};
			try {
				const userResponse = await browser.requestSingle( pageRequest );
				throw log.error( "should have failed...", { userResponse } );
			} catch ( _err ) {
				if ( _err.response != null && _err.request && _err instanceof Error ) {
					const axiosErr = _err as xlib.net.axios.AxiosError;
					log.assert( axiosErr.response != null && axiosErr.response.status === 424, "expected error status 424", { axiosErr } );
				} else {
					throw _err;
				}
			}
		} ).timeout( 20000 );

		it2( async function invalidPort() {
			let pageRequest: ioDatatypes.IPageRequest = {
				url: "https://www.example.com:82728",
				renderType: "plainText",
				requestSettings: { maxWait: 3000 },
			};
			try {
				const userResponse = await browser.requestSingle( pageRequest );
				log.error( "should have failed...", { userResponse } );
				throw new Error( "the request should have failed due to invalid URL port" );
			} catch ( _err ) {
				if ( _err.response != null && _err.request && _err instanceof Error ) {
					const axiosErr = _err as xlib.net.axios.AxiosError;
					log.assert( axiosErr.response != null && axiosErr.response.status === 424, "expected error status 424", { axiosErr } );
				} else {
					throw _err;
				}
			}
		} ).timeout( 6000 );

		it2( async function invalidUrl() {
			let pageRequest: ioDatatypes.IPageRequest = {
				url: "//example.com",
			};
			try {
				const userResponse = await browser.requestSingle( pageRequest );
				log.error( "should have failed...", { userResponse } );
				throw new Error( "the request should have failed due to invalid URL" );
			} catch ( _err ) {
				if ( _err.response != null && _err.request && _err instanceof Error ) {
					const axiosErr = _err as xlib.net.axios.AxiosError;
					log.assert( axiosErr.response != null && axiosErr.response.status === 400, "expected error status 400", { axiosErr } );
				} else {
					throw _err;
				}
			}
		} ).timeout( 6000 );


		it2( async function invalidUrlFile() {
			let pageRequest: ioDatatypes.IPageRequest = {
				url: "file://.",
			};
			try {
				const userResponse = await browser.requestSingle( pageRequest );
				log.error( "should have failed...", { userResponse } );
				throw new Error( "the request should have failed due to invalid URL" );
			} catch ( _err ) {
				if ( _err.response != null && _err.request && _err instanceof Error ) {
					const axiosErr = _err as xlib.net.axios.AxiosError;
					log.assert( axiosErr.response != null && axiosErr.response.status === 400, "expected error status 400", { axiosErr } );
				} else {
					throw _err;
				}
			}
		} ).timeout( 6000 );


		it2( async function allNullParameters() {
			let pageRequest = ioDatatypes.pageRequestDefaultsGet();
			pageRequest.url = "http://localhost/examples/corpus/example.com.html";
			_nullAllProperties( pageRequest );

			try {
				const userResponse = await browser.requestSingle( pageRequest );
				log.error( "should have failed...", { userResponse } );
				throw new Error( "the request should have failed due to invalid URL" );
			} catch ( _err ) {
				if ( _err.response != null && _err.request && _err instanceof Error ) {
					const axiosErr = _err as xlib.net.axios.AxiosError;
					log.assert( axiosErr.response != null && axiosErr.response.status === 400, "expected error status 400", { axiosErr } );
				} else {
					throw _err;
				}
			}
		} );

		it2( async function scriptFailureInjected() {
			const pageRequest: ioDatatypes.IPageRequest = {
				url: "http://localhost/examples/corpus/simple.html",
				backend: "chrome",
				outputAsJson: true,
				renderType: "script",
				scripts: {
					loadFinished: [
						"throw new Error('boom');",
					],
				},
			};
			const response = await browser.requestSingle( pageRequest );
			verifyResponseStatus( response, { contentStatusCode: 424 } );//, doneDetail: "error:scripts.loadFinished" } );
		} ).timeout( 6000 );

		it2( async function scriptFailureLoad() {
			const pageRequest: ioDatatypes.IPageRequest = {
				url: "http://localhost/examples/corpus/simple.html",
				backend: "chrome",
				outputAsJson: true,
				renderType: "script",
				scripts: {
					domReady: [ "http://localhost/examples/scripts/does-not-exist.js", ],
				},
			};
			const response = await browser.requestSingle( pageRequest );
			verifyResponseStatus( response, { contentStatusCode: 424 } );//, doneDetail: "error:scripts.domReady" } );
		} ).timeout( 6000 );



	} ); //end describe("failure tests");


} );//end describe(__filename);
