UNPKG

37.2 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const blocking_proxy_1 = require("blocking-proxy");
4const selenium_webdriver_1 = require("selenium-webdriver");
5const url = require("url");
6const webdriver_js_extender_1 = require("webdriver-js-extender");
7const element_1 = require("./element");
8const expectedConditions_1 = require("./expectedConditions");
9const locators_1 = require("./locators");
10const logger_1 = require("./logger");
11const clientSideScripts = require('./clientsidescripts');
12// TODO: fix the typings for selenium-webdriver/lib/command
13const Command = require('selenium-webdriver/lib/command').Command;
14const CommandName = require('selenium-webdriver/lib/command').Name;
15// jshint browser: true
16const DEFER_LABEL = 'NG_DEFER_BOOTSTRAP!';
17const DEFAULT_RESET_URL = 'data:text/html,<html></html>';
18const DEFAULT_GET_PAGE_TIMEOUT = 10000;
19let logger = new logger_1.Logger('protractor');
20// TODO(cnishina): either remove for loop entirely since this does not export anything
21// the user might need since everything is composed (with caveat that this could be a
22// potential breaking change) or export the types with `export * from 'selenium-webdriver'`;
23/*
24 * Mix in other webdriver functionality to be accessible via protractor.
25 */
26for (let foo in require('selenium-webdriver')) {
27 exports[foo] = require('selenium-webdriver')[foo];
28}
29// Explicitly define types for webdriver.WebDriver and ExtendedWebDriver.
30// We do this because we use composition over inheritance to implement polymorphism, and therefore
31// we don't want to inherit WebDriver's constructor.
32class AbstractWebDriver {
33}
34exports.AbstractWebDriver = AbstractWebDriver;
35class AbstractExtendedWebDriver extends AbstractWebDriver {
36}
37exports.AbstractExtendedWebDriver = AbstractExtendedWebDriver;
38/**
39 * Mix a function from one object onto another. The function will still be
40 * called in the context of the original object. Any arguments of type
41 * `ElementFinder` will be unwrapped to their underlying `WebElement` instance
42 *
43 * @private
44 * @param {Object} to
45 * @param {Object} from
46 * @param {string} fnName
47 * @param {function=} setupFn
48 */
49function ptorMixin(to, from, fnName, setupFn) {
50 to[fnName] = function () {
51 const args = arguments;
52 for (let i = 0; i < args.length; i++) {
53 if (args[i] instanceof element_1.ElementFinder) {
54 args[i] = args[i].getWebElement();
55 }
56 }
57 const run = () => {
58 return from[fnName].apply(from, args);
59 };
60 if (setupFn) {
61 const setupResult = setupFn();
62 if (setupResult && (typeof setupResult.then === 'function')) {
63 return setupResult.then(run);
64 }
65 }
66 return run();
67 };
68}
69;
70/**
71 * Build the helper 'element' function for a given instance of Browser.
72 *
73 * @private
74 * @param {Browser} browser A browser instance.
75 * @returns {function(webdriver.Locator): ElementFinder}
76 */
77function buildElementHelper(browser) {
78 let element = ((locator) => {
79 return new element_1.ElementArrayFinder(browser).all(locator).toElementFinder_();
80 });
81 element.all = (locator) => {
82 return new element_1.ElementArrayFinder(browser).all(locator);
83 };
84 return element;
85}
86;
87/**
88 * @alias browser
89 * @constructor
90 * @extends {webdriver_extensions.ExtendedWebDriver}
91 * @param {webdriver.WebDriver} webdriver
92 * @param {string=} opt_baseUrl A base URL to run get requests against.
93 * @param {string|webdriver.promise.Promise<string>=} opt_rootElement Selector element that has an
94 * ng-app in scope.
95 * @param {boolean=} opt_untrackOutstandingTimeouts Whether Protractor should
96 * stop tracking outstanding $timeouts.
97 */
98class ProtractorBrowser extends AbstractExtendedWebDriver {
99 constructor(webdriverInstance, opt_baseUrl, opt_rootElement, opt_untrackOutstandingTimeouts, opt_blockingProxyUrl) {
100 super();
101 // These functions should delegate to the webdriver instance, but should
102 // wait for Angular to sync up before performing the action. This does not
103 // include functions which are overridden by protractor below.
104 let methodsToSync = ['getCurrentUrl', 'getPageSource', 'getTitle'];
105 let extendWDInstance;
106 try {
107 extendWDInstance = webdriver_js_extender_1.extend(webdriverInstance);
108 }
109 catch (e) {
110 // Probably not a driver that can be extended (e.g. gotten using
111 // `directConnect: true` in the config)
112 extendWDInstance = webdriverInstance;
113 }
114 // Mix all other driver functionality into Protractor.
115 Object.getOwnPropertyNames(selenium_webdriver_1.WebDriver.prototype).forEach(method => {
116 if (!this[method] && typeof extendWDInstance[method] === 'function') {
117 if (methodsToSync.indexOf(method) !== -1) {
118 ptorMixin(this, extendWDInstance, method, this.waitForAngular.bind(this));
119 }
120 else {
121 ptorMixin(this, extendWDInstance, method);
122 }
123 }
124 });
125 this.driver = extendWDInstance;
126 if (opt_blockingProxyUrl) {
127 logger.info('Starting BP client for ' + opt_blockingProxyUrl);
128 this.bpClient = new blocking_proxy_1.BPClient(opt_blockingProxyUrl);
129 }
130 this.element = buildElementHelper(this);
131 this.$ = element_1.build$(this.element, selenium_webdriver_1.By);
132 this.$$ = element_1.build$$(this.element, selenium_webdriver_1.By);
133 this.baseUrl = opt_baseUrl || '';
134 this.getPageTimeout = DEFAULT_GET_PAGE_TIMEOUT;
135 this.params = {};
136 this.resetUrl = DEFAULT_RESET_URL;
137 let ng12Hybrid_ = false;
138 Object.defineProperty(this, 'ng12Hybrid', {
139 get: function () {
140 return ng12Hybrid_;
141 },
142 set: function (ng12Hybrid) {
143 if (ng12Hybrid) {
144 logger.warn('You have set ng12Hybrid. As of Protractor 4.1.0, ' +
145 'Protractor can automatically infer if you are using an ' +
146 'ngUpgrade app (as long as ng1 is loaded before you call ' +
147 'platformBrowserDynamic()), and this flag is no longer needed ' +
148 'for most users');
149 }
150 ng12Hybrid_ = ng12Hybrid;
151 }
152 });
153 this.ready = this.angularAppRoot(opt_rootElement || '')
154 .then(() => {
155 return this.driver.getSession();
156 })
157 .then((session) => {
158 // Internet Explorer does not accept data URLs, which are the default
159 // reset URL for Protractor.
160 // Safari accepts data urls, but SafariDriver fails after one is used.
161 // PhantomJS produces a "Detected a page unload event" if we use data urls
162 let browserName = session.getCapabilities().get('browserName');
163 if (browserName === 'internet explorer' || browserName === 'safari' ||
164 browserName === 'phantomjs' || browserName === 'MicrosoftEdge') {
165 this.resetUrl = 'about:blank';
166 }
167 return this;
168 });
169 this.trackOutstandingTimeouts_ = !opt_untrackOutstandingTimeouts;
170 this.mockModules_ = [];
171 this.addBaseMockModules_();
172 // set up expected conditions
173 this.ExpectedConditions = new expectedConditions_1.ProtractorExpectedConditions(this);
174 }
175 /**
176 * The css selector for an element on which to find Angular. This is usually
177 * 'body' but if your ng-app is on a subsection of the page it may be
178 * a subelement.
179 *
180 * This property is deprecated - please use angularAppRoot() instead.
181 *
182 * @deprecated
183 * @type {string}
184 */
185 set rootEl(value) {
186 this.angularAppRoot(value);
187 }
188 get rootEl() {
189 return this.internalRootEl;
190 }
191 /**
192 * Set the css selector for an element on which to find Angular. This is usually
193 * 'body' but if your ng-app is on a subsection of the page it may be
194 * a subelement.
195 *
196 * The change will be made within WebDriver's control flow, so that commands after
197 * this method is called use the new app root. Pass nothing to get a promise that
198 * resolves to the value of the selector.
199 *
200 * @param {string|webdriver.promise.Promise<string>} value The new selector.
201 * @returns A promise that resolves with the value of the selector.
202 */
203 angularAppRoot(value = null) {
204 return this.driver.controlFlow().execute(() => {
205 if (value != null) {
206 return selenium_webdriver_1.promise.when(value).then((value) => {
207 this.internalRootEl = value;
208 if (this.bpClient) {
209 const bpCommandPromise = this.bpClient.setWaitParams(value);
210 // Convert to webdriver promise as best as possible
211 return selenium_webdriver_1.promise.when(bpCommandPromise).then(() => this.internalRootEl);
212 }
213 return this.internalRootEl;
214 });
215 }
216 return selenium_webdriver_1.promise.when(this.internalRootEl);
217 }, `Set angular root selector to ${value}`);
218 }
219 /**
220 * If true, Protractor will not attempt to synchronize with the page before
221 * performing actions. This can be harmful because Protractor will not wait
222 * until $timeouts and $http calls have been processed, which can cause
223 * tests to become flaky. This should be used only when necessary, such as
224 * when a page continuously polls an API using $timeout.
225 *
226 * Initialized to `false` by the runner.
227 *
228 * This property is deprecated - please use waitForAngularEnabled instead.
229 *
230 * @deprecated
231 * @type {boolean}
232 */
233 set ignoreSynchronization(value) {
234 this.waitForAngularEnabled(!value);
235 }
236 get ignoreSynchronization() {
237 return this.internalIgnoreSynchronization;
238 }
239 /**
240 * If set to false, Protractor will not wait for Angular $http and $timeout
241 * tasks to complete before interacting with the browser. This can cause
242 * flaky tests, but should be used if, for instance, your app continuously
243 * polls an API with $timeout.
244 *
245 * Call waitForAngularEnabled() without passing a value to read the current
246 * state without changing it.
247 */
248 waitForAngularEnabled(enabled = null) {
249 if (enabled != null) {
250 const ret = this.driver.controlFlow().execute(() => {
251 return selenium_webdriver_1.promise.when(enabled).then((enabled) => {
252 if (this.bpClient) {
253 logger.debug('Setting waitForAngular' + !enabled);
254 const bpCommandPromise = this.bpClient.setWaitEnabled(enabled);
255 // Convert to webdriver promise as best as possible
256 return selenium_webdriver_1.promise.when(bpCommandPromise).then(() => enabled);
257 }
258 });
259 }, `Set proxy synchronization enabled to ${enabled}`);
260 this.internalIgnoreSynchronization = !enabled;
261 return ret;
262 }
263 return selenium_webdriver_1.promise.when(!this.ignoreSynchronization);
264 }
265 /**
266 * Get the processed configuration object that is currently being run. This
267 * will contain the specs and capabilities properties of the current runner
268 * instance.
269 *
270 * Set by the runner.
271 *
272 * @returns {webdriver.promise.Promise} A promise which resolves to the
273 * capabilities object.
274 */
275 getProcessedConfig() {
276 return null;
277 }
278 /**
279 * Fork another instance of browser for use in interactive tests.
280 *
281 * @example
282 * // Running with control flow enabled
283 * var fork = browser.forkNewDriverInstance();
284 * fork.get('page1'); // 'page1' gotten by forked browser
285 *
286 * // Running with control flow disabled
287 * var forked = await browser.forkNewDriverInstance().ready;
288 * await forked.get('page1'); // 'page1' gotten by forked browser
289 *
290 * @param {boolean=} useSameUrl Whether to navigate to current url on creation
291 * @param {boolean=} copyMockModules Whether to apply same mock modules on creation
292 * @param {boolean=} copyConfigUpdates Whether to copy over changes to `baseUrl` and similar
293 * properties initialized to values in the the config. Defaults to `true`
294 *
295 * @returns {ProtractorBrowser} A browser instance.
296 */
297 forkNewDriverInstance(useSameUrl, copyMockModules, copyConfigUpdates = true) {
298 return null;
299 }
300 /**
301 * Restart the browser. This is done by closing this browser instance and creating a new one.
302 * A promise resolving to the new instance is returned, and if this function was called on the
303 * global `browser` instance then Protractor will automatically overwrite the global `browser`
304 * variable.
305 *
306 * When restarting a forked browser, it is the caller's job to overwrite references to the old
307 * instance.
308 *
309 * This function behaves slightly differently depending on if the webdriver control flow is
310 * enabled. If the control flow is enabled, the global `browser` object is synchronously
311 * replaced. If the control flow is disabled, the global `browser` is replaced asynchronously
312 * after the old driver quits.
313 *
314 * Set by the runner.
315 *
316 * @example
317 * // Running against global browser, with control flow enabled
318 * browser.get('page1');
319 * browser.restart();
320 * browser.get('page2'); // 'page2' gotten by restarted browser
321 *
322 * // Running against global browser, with control flow disabled
323 * await browser.get('page1');
324 * await browser.restart();
325 * await browser.get('page2'); // 'page2' gotten by restarted browser
326 *
327 * // Running against forked browsers, with the control flow enabled
328 * // In this case, you may prefer `restartSync` (documented below)
329 * var forked = browser.forkNewDriverInstance();
330 * fork.get('page1');
331 * fork.restart().then(function(fork) {
332 * fork.get('page2'); // 'page2' gotten by restarted fork
333 * });
334 *
335 * // Running against forked browsers, with the control flow disabled
336 * var forked = await browser.forkNewDriverInstance().ready;
337 * await fork.get('page1');
338 * fork = await fork.restart();
339 * await fork.get('page2'); // 'page2' gotten by restarted fork
340 *
341 * // Unexpected behavior can occur if you save references to the global `browser`
342 * var savedBrowser = browser;
343 * browser.get('foo').then(function() {
344 * console.log(browser === savedBrowser); // false
345 * });
346 * browser.restart();
347 *
348 * @returns {webdriver.promise.Promise<ProtractorBrowser>} A promise resolving to the restarted
349 * browser
350 */
351 restart() {
352 return;
353 }
354 /**
355 * Like `restart`, but instead of returning a promise resolving to the new browser instance,
356 * returns the new browser instance directly. Can only be used when the control flow is enabled.
357 *
358 * @example
359 * // Running against global browser
360 * browser.get('page1');
361 * browser.restartSync();
362 * browser.get('page2'); // 'page2' gotten by restarted browser
363 *
364 * // Running against forked browsers
365 * var forked = browser.forkNewDriverInstance();
366 * fork.get('page1');
367 * fork = fork.restartSync();
368 * fork.get('page2'); // 'page2' gotten by restarted fork
369 *
370 * @throws {TypeError} Will throw an error if the control flow is not enabled
371 * @returns {ProtractorBrowser} The restarted browser
372 */
373 restartSync() {
374 return;
375 }
376 /**
377 * Instead of using a single root element, search through all angular apps
378 * available on the page when finding elements or waiting for stability.
379 * Only compatible with Angular2.
380 */
381 useAllAngular2AppRoots() {
382 // The empty string is an invalid css selector, so we use it to easily
383 // signal to scripts to not find a root element.
384 this.angularAppRoot('');
385 }
386 /**
387 * The same as {@code webdriver.WebDriver.prototype.executeScript},
388 * but with a customized description for debugging.
389 *
390 * @private
391 * @param {!(string|Function)} script The script to execute.
392 * @param {string} description A description of the command for debugging.
393 * @param {...*} var_args The arguments to pass to the script.
394 * @returns {!webdriver.promise.Promise.<T>} A promise that will resolve to
395 * the scripts return value.
396 * @template T
397 */
398 executeScriptWithDescription(script, description, ...scriptArgs) {
399 if (typeof script === 'function') {
400 script = 'return (' + script + ').apply(null, arguments);';
401 }
402 return this.driver.schedule(new Command(CommandName.EXECUTE_SCRIPT)
403 .setParameter('script', script)
404 .setParameter('args', scriptArgs), description);
405 }
406 /**
407 * The same as {@code webdriver.WebDriver.prototype.executeAsyncScript},
408 * but with a customized description for debugging.
409 *
410 * @private
411 * @param {!(string|Function)} script The script to execute.
412 * @param {string} description A description for debugging purposes.
413 * @param {...*} var_args The arguments to pass to the script.
414 * @returns {!webdriver.promise.Promise.<T>} A promise that will resolve to
415 * the
416 * scripts return value.
417 * @template T
418 */
419 executeAsyncScript_(script, description, ...scriptArgs) {
420 if (typeof script === 'function') {
421 script = 'return (' + script + ').apply(null, arguments);';
422 }
423 return this.driver.schedule(new Command(CommandName.EXECUTE_ASYNC_SCRIPT)
424 .setParameter('script', script)
425 .setParameter('args', scriptArgs), description);
426 }
427 /**
428 * Instruct webdriver to wait until Angular has finished rendering and has
429 * no outstanding $http or $timeout calls before continuing.
430 * Note that Protractor automatically applies this command before every
431 * WebDriver action.
432 *
433 * @param {string=} opt_description An optional description to be added
434 * to webdriver logs.
435 * @returns {!webdriver.promise.Promise} A promise that will resolve to the
436 * scripts return value.
437 */
438 waitForAngular(opt_description) {
439 let description = opt_description ? ' - ' + opt_description : '';
440 if (this.ignoreSynchronization) {
441 return this.driver.controlFlow().execute(() => {
442 return true;
443 }, 'Ignore Synchronization Protractor.waitForAngular()');
444 }
445 let runWaitForAngularScript = () => {
446 if (this.plugins_.skipAngularStability() || this.bpClient) {
447 return this.driver.controlFlow().execute(() => {
448 return selenium_webdriver_1.promise.when(null);
449 }, 'bpClient or plugin stability override');
450 }
451 else {
452 // Need to wrap this so that we read rootEl in the control flow, not synchronously.
453 return this.angularAppRoot().then((rootEl) => {
454 return this.executeAsyncScript_(clientSideScripts.waitForAngular, 'Protractor.waitForAngular()' + description, rootEl);
455 });
456 }
457 };
458 return runWaitForAngularScript()
459 .then((browserErr) => {
460 if (browserErr) {
461 throw new Error('Error while waiting for Protractor to ' +
462 'sync with the page: ' + JSON.stringify(browserErr));
463 }
464 })
465 .then(() => {
466 return this.driver.controlFlow()
467 .execute(() => {
468 return this.plugins_.waitForPromise(this);
469 }, 'Plugins.waitForPromise()')
470 .then(() => {
471 return this.driver.wait(() => {
472 return this.plugins_.waitForCondition(this).then((results) => {
473 return results.reduce((x, y) => x && y, true);
474 });
475 }, this.allScriptsTimeout, 'Plugins.waitForCondition()');
476 });
477 }, (err) => {
478 let timeout;
479 if (/asynchronous script timeout/.test(err.message)) {
480 // Timeout on Chrome
481 timeout = /-?[\d\.]*\ seconds/.exec(err.message);
482 }
483 else if (/Timed out waiting for async script/.test(err.message)) {
484 // Timeout on Firefox
485 timeout = /-?[\d\.]*ms/.exec(err.message);
486 }
487 else if (/Timed out waiting for an asynchronous script/.test(err.message)) {
488 // Timeout on Safari
489 timeout = /-?[\d\.]*\ ms/.exec(err.message);
490 }
491 if (timeout) {
492 let errMsg = `Timed out waiting for asynchronous Angular tasks to finish after ` +
493 `${timeout}. This may be because the current page is not an Angular ` +
494 `application. Please see the FAQ for more details: ` +
495 `https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular`;
496 if (description.indexOf(' - Locator: ') == 0) {
497 errMsg += '\nWhile waiting for element with locator' + description;
498 }
499 let pendingTimeoutsPromise;
500 if (this.trackOutstandingTimeouts_) {
501 pendingTimeoutsPromise = this.executeScriptWithDescription('return window.NG_PENDING_TIMEOUTS', 'Protractor.waitForAngular() - getting pending timeouts' + description);
502 }
503 else {
504 pendingTimeoutsPromise = selenium_webdriver_1.promise.when({});
505 }
506 let pendingHttpsPromise = this.executeScriptWithDescription(clientSideScripts.getPendingHttpRequests, 'Protractor.waitForAngular() - getting pending https' + description, this.internalRootEl);
507 return selenium_webdriver_1.promise.all([pendingTimeoutsPromise, pendingHttpsPromise])
508 .then((arr) => {
509 let pendingTimeouts = arr[0] || [];
510 let pendingHttps = arr[1] || [];
511 let key, pendingTasks = [];
512 for (key in pendingTimeouts) {
513 if (pendingTimeouts.hasOwnProperty(key)) {
514 pendingTasks.push(' - $timeout: ' + pendingTimeouts[key]);
515 }
516 }
517 for (key in pendingHttps) {
518 pendingTasks.push(' - $http: ' + pendingHttps[key].url);
519 }
520 if (pendingTasks.length) {
521 errMsg += '. \nThe following tasks were pending:\n';
522 errMsg += pendingTasks.join('\n');
523 }
524 err.message = errMsg;
525 throw err;
526 }, () => {
527 err.message = errMsg;
528 throw err;
529 });
530 }
531 else {
532 throw err;
533 }
534 });
535 }
536 /**
537 * Waits for Angular to finish rendering before searching for elements.
538 * @see webdriver.WebDriver.findElement
539 * @returns {!webdriver.WebElementPromise} A promise that will be resolved to
540 * the located {@link webdriver.WebElement}.
541 */
542 findElement(locator) {
543 return this.element(locator).getWebElement();
544 }
545 /**
546 * Waits for Angular to finish rendering before searching for elements.
547 * @see webdriver.WebDriver.findElements
548 * @returns {!webdriver.promise.Promise} A promise that will be resolved to an
549 * array of the located {@link webdriver.WebElement}s.
550 */
551 findElements(locator) {
552 return this.element.all(locator).getWebElements();
553 }
554 /**
555 * Tests if an element is present on the page.
556 * @see webdriver.WebDriver.isElementPresent
557 * @returns {!webdriver.promise.Promise} A promise that will resolve to whether
558 * the element is present on the page.
559 */
560 isElementPresent(locatorOrElement) {
561 let element;
562 if (locatorOrElement instanceof element_1.ElementFinder) {
563 element = locatorOrElement;
564 }
565 else if (locatorOrElement instanceof selenium_webdriver_1.WebElement) {
566 element = element_1.ElementFinder.fromWebElement_(this, locatorOrElement);
567 }
568 else {
569 element = this.element(locatorOrElement);
570 }
571 return element.isPresent();
572 }
573 /**
574 * Add a module to load before Angular whenever Protractor.get is called.
575 * Modules will be registered after existing modules already on the page,
576 * so any module registered here will override preexisting modules with the
577 * same name.
578 *
579 * @example
580 * browser.addMockModule('modName', function() {
581 * angular.module('modName', []).value('foo', 'bar');
582 * });
583 *
584 * @param {!string} name The name of the module to load or override.
585 * @param {!string|Function} script The JavaScript to load the module.
586 * Note that this will be executed in the browser context, so it cannot
587 * access variables from outside its scope.
588 * @param {...*} varArgs Any additional arguments will be provided to
589 * the script and may be referenced using the `arguments` object.
590 */
591 addMockModule(name, script, ...moduleArgs) {
592 this.mockModules_.push({ name: name, script: script, args: moduleArgs });
593 }
594 /**
595 * Clear the list of registered mock modules.
596 */
597 clearMockModules() {
598 this.mockModules_ = [];
599 this.addBaseMockModules_();
600 }
601 /**
602 * Remove a registered mock module.
603 *
604 * @example
605 * browser.removeMockModule('modName');
606 *
607 * @param {!string} name The name of the module to remove.
608 */
609 removeMockModule(name) {
610 for (let i = 0; i < this.mockModules_.length; ++i) {
611 if (this.mockModules_[i].name == name) {
612 this.mockModules_.splice(i--, 1);
613 }
614 }
615 }
616 /**
617 * Get a list of the current mock modules.
618 *
619 * @returns {Array.<!string|Function>} The list of mock modules.
620 */
621 getRegisteredMockModules() {
622 return this.mockModules_.map(module => module.script);
623 }
624 ;
625 /**
626 * Add the base mock modules used for all Protractor tests.
627 *
628 * @private
629 */
630 addBaseMockModules_() {
631 this.addMockModule('protractorBaseModule_', clientSideScripts.protractorBaseModuleFn, this.trackOutstandingTimeouts_);
632 }
633 /**
634 * @see webdriver.WebDriver.get
635 *
636 * Navigate to the given destination and loads mock modules before
637 * Angular. Assumes that the page being loaded uses Angular.
638 * If you need to access a page which does not have Angular on load, use
639 * the wrapped webdriver directly.
640 *
641 * @example
642 * browser.get('https://angularjs.org/');
643 * expect(browser.getCurrentUrl()).toBe('https://angularjs.org/');
644 *
645 * @param {string} destination Destination URL.
646 * @param {number=} opt_timeout Number of milliseconds to wait for Angular to
647 * start.
648 */
649 get(destination, timeout = this.getPageTimeout) {
650 destination = this.baseUrl.indexOf('file://') === 0 ? this.baseUrl + destination :
651 url.resolve(this.baseUrl, destination);
652 if (this.ignoreSynchronization) {
653 return this.driver.get(destination)
654 .then(() => this.driver.controlFlow().execute(() => this.plugins_.onPageLoad(this)))
655 .then(() => null);
656 }
657 let msg = (str) => {
658 return 'Protractor.get(' + destination + ') - ' + str;
659 };
660 return this.driver.controlFlow()
661 .execute(() => {
662 return selenium_webdriver_1.promise.when(null);
663 })
664 .then(() => {
665 if (this.bpClient) {
666 return this.driver.controlFlow().execute(() => {
667 return this.bpClient.setWaitEnabled(false);
668 });
669 }
670 })
671 .then(() => {
672 // Go to reset url
673 return this.driver.get(this.resetUrl);
674 })
675 .then(() => {
676 // Set defer label and navigate
677 return this.executeScriptWithDescription('window.name = "' + DEFER_LABEL + '" + window.name;' +
678 'window.location.replace("' + destination + '");', msg('reset url'));
679 })
680 .then(() => {
681 // We need to make sure the new url has loaded before
682 // we try to execute any asynchronous scripts.
683 return this.driver.wait(() => {
684 return this.executeScriptWithDescription('return window.location.href;', msg('get url'))
685 .then((url) => {
686 return url !== this.resetUrl;
687 }, (err) => {
688 if (err.code == 13 || err.name === 'JavascriptError') {
689 // Ignore the error, and continue trying. This is
690 // because IE driver sometimes (~1%) will throw an
691 // unknown error from this execution. See
692 // https://github.com/angular/protractor/issues/841
693 // This shouldn't mask errors because it will fail
694 // with the timeout anyway.
695 return false;
696 }
697 else {
698 throw err;
699 }
700 });
701 }, timeout, 'waiting for page to load for ' + timeout + 'ms');
702 })
703 .then(() => {
704 // Run Plugins
705 return this.driver.controlFlow().execute(() => {
706 return this.plugins_.onPageLoad(this);
707 });
708 })
709 .then(() => {
710 // Make sure the page is an Angular page.
711 return this
712 .executeAsyncScript_(clientSideScripts.testForAngular, msg('test for angular'), Math.floor(timeout / 1000), this.ng12Hybrid)
713 .then((angularTestResult) => {
714 let angularVersion = angularTestResult.ver;
715 if (!angularVersion) {
716 let message = angularTestResult.message;
717 logger.error(`Could not find Angular on page ${destination} : ${message}`);
718 throw new Error(`Angular could not be found on the page ${destination}. ` +
719 `If this is not an Angular application, you may need to turn off waiting for Angular.
720 Please see
721 https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular-on-page-load`);
722 }
723 return angularVersion;
724 }, (err) => {
725 throw new Error('Error while running testForAngular: ' + err.message);
726 });
727 })
728 .then((angularVersion) => {
729 // Load Angular Mocks
730 if (angularVersion === 1) {
731 // At this point, Angular will pause for us until angular.resumeBootstrap is called.
732 let moduleNames = [];
733 let modulePromise = selenium_webdriver_1.promise.when(null);
734 for (const { name, script, args } of this.mockModules_) {
735 moduleNames.push(name);
736 let executeScriptArgs = [script, msg('add mock module ' + name), ...args];
737 modulePromise = modulePromise.then(() => this.executeScriptWithDescription.apply(this, executeScriptArgs)
738 .then(null, (err) => {
739 throw new Error('Error while running module script ' + name + ': ' + err.message);
740 }));
741 }
742 return modulePromise.then(() => this.executeScriptWithDescription('window.__TESTABILITY__NG1_APP_ROOT_INJECTOR__ = ' +
743 'angular.resumeBootstrap(arguments[0]);', msg('resume bootstrap'), moduleNames));
744 }
745 else {
746 // TODO: support mock modules in Angular2. For now, error if someone
747 // has tried to use one.
748 if (this.mockModules_.length > 1) {
749 throw 'Trying to load mock modules on an Angular v2+ app is not yet supported.';
750 }
751 }
752 })
753 .then(() => {
754 // Reset bpClient sync
755 if (this.bpClient) {
756 return this.driver.controlFlow().execute(() => {
757 return this.bpClient.setWaitEnabled(!this.internalIgnoreSynchronization);
758 });
759 }
760 })
761 .then(() => {
762 // Run Plugins
763 return this.driver.controlFlow().execute(() => {
764 return this.plugins_.onPageStable(this);
765 });
766 })
767 .then(() => null);
768 }
769 /**
770 * @see webdriver.WebDriver.refresh
771 *
772 * Makes a full reload of the current page and loads mock modules before
773 * Angular. Assumes that the page being loaded uses Angular.
774 * If you need to access a page which does not have Angular on load, use
775 * the wrapped webdriver directly.
776 *
777 * @param {number=} opt_timeout Number of milliseconds to wait for Angular to start.
778 */
779 refresh(opt_timeout) {
780 if (this.ignoreSynchronization) {
781 return this.driver.navigate().refresh();
782 }
783 return this
784 .executeScriptWithDescription('return window.location.href', 'Protractor.refresh() - getUrl')
785 .then((href) => {
786 return this.get(href, opt_timeout);
787 });
788 }
789 /**
790 * Mixin navigation methods back into the navigation object so that
791 * they are invoked as before, i.e. driver.navigate().refresh()
792 */
793 navigate() {
794 let nav = this.driver.navigate();
795 ptorMixin(nav, this, 'refresh');
796 return nav;
797 }
798 /**
799 * Browse to another page using in-page navigation.
800 *
801 * @example
802 * browser.get('http://angular.github.io/protractor/#/tutorial');
803 * browser.setLocation('api');
804 * expect(browser.getCurrentUrl())
805 * .toBe('http://angular.github.io/protractor/#/api');
806 *
807 * @param {string} url In page URL using the same syntax as $location.url()
808 * @returns {!webdriver.promise.Promise} A promise that will resolve once
809 * page has been changed.
810 */
811 setLocation(url) {
812 return this.waitForAngular()
813 .then(() => this.angularAppRoot())
814 .then((rootEl) => this.executeScriptWithDescription(clientSideScripts.setLocation, 'Protractor.setLocation()', rootEl, url)
815 .then((browserErr) => {
816 if (browserErr) {
817 throw 'Error while navigating to \'' + url +
818 '\' : ' + JSON.stringify(browserErr);
819 }
820 }));
821 }
822 /**
823 * Deprecated, use `browser.getCurrentUrl()` instead.
824 *
825 * Despite its name, this function will generally return `$location.url()`, though in some
826 * cases it will return `$location.absUrl()` instead. This function is only here for legacy
827 * users, and will probably be removed in Protractor 6.0.
828 *
829 * @deprecated Please use `browser.getCurrentUrl()`
830 * @example
831 * browser.get('http://angular.github.io/protractor/#/api');
832 * expect(browser.getLocationAbsUrl())
833 * .toBe('http://angular.github.io/protractor/#/api');
834 * @returns {webdriver.promise.Promise<string>} The current absolute url from
835 * AngularJS.
836 */
837 getLocationAbsUrl() {
838 logger.warn('`browser.getLocationAbsUrl()` is deprecated, please use `browser.getCurrentUrl` instead.');
839 return this.waitForAngular()
840 .then(() => this.angularAppRoot())
841 .then((rootEl) => this.executeScriptWithDescription(clientSideScripts.getLocationAbsUrl, 'Protractor.getLocationAbsUrl()', rootEl));
842 }
843 /**
844 * Determine if the control flow is enabled.
845 *
846 * @returns true if the control flow is enabled, false otherwise.
847 */
848 controlFlowIsEnabled() {
849 if (selenium_webdriver_1.promise.USE_PROMISE_MANAGER !== undefined) {
850 return selenium_webdriver_1.promise.USE_PROMISE_MANAGER;
851 }
852 else {
853 // True for old versions of `selenium-webdriver`, probably false in >=5.0.0
854 return !!selenium_webdriver_1.promise.ControlFlow;
855 }
856 }
857}
858/**
859 * @type {ProtractorBy}
860 */
861ProtractorBrowser.By = new locators_1.ProtractorBy();
862exports.ProtractorBrowser = ProtractorBrowser;
863//# sourceMappingURL=browser.js.map
\No newline at end of file