Coder Social home page Coder Social logo

gogo666 / cdp4j_tests Goto Github PK

View Code? Open in Web Editor NEW

This project forked from sergueik/cdp4j_tests

0.0 1.0 0.0 151 KB

(re)construct Selenium-like code patterns with Chrome DevTools Protocol Java client

License: MIT License

Java 87.13% JavaScript 12.43% HTML 0.44%

cdp4j_tests's Introduction

Info

The Chrome Devtools project offers an alternative powerful set of API to manage te browser, and appears currently targeted primarily for Javascript developers.

This project exercises the Java client of Chrome DevTools Protocol trying to (re)construct code patterns familiar to a Selenium developer to reduce the effort of rewriting existing Selenium -based test suite to CDP backend.

A massive test suite developed earlier practicing various publicly available practice pages is uses as a reference.

The project is in active development, so please bookmark and check for updates.

Examples

The following code fragments familiar to a Selenium developer are implemented on top of CDP and demonstrated:

  • Iterate over and filter the set of elements, perform further action with specific members similar to Selenium findElements:
		Launcher launcher = new Launcher();
		SessionFactory factory = launcher.launch();
		session = factory.create();
		// install extensions
		session.installSizzle();
		session.useSizzle();
		session.clearCookies();
		session.clearCache();
		session.setUserAgent(
				"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/534.34 (KHTML, like Gecko) PhantomJS/1.9.7 Safari/534.34");
		session.navigate("https://webfolder.io");

    // examine navbar links
		String cssSelector = "#nav a";

		// Arrange
		session.waitUntil(o -> o.getObjectIds(cssSelector).size() > 0, 1000, 100);
		// Act
		String id = session.getObjectIds(cssSelector).stream().filter(_id ->
		((String) session.getPropertyByObjectId(_id, "href")).matches(".*about.html$")).collect(Collectors.toList()).get(0);
		// Assert
		Assert.assertEquals(session.getPropertyByObjectId(id, "href"),
				"https://webfolder.io/about.html");
		Assert.assertEquals(session.getPropertyByObjectId(id, "innerHTML"),
				"About");
  • Highlight the element of interest by applying a border style:
	protected void highlight(String selectorOfElement, Session session,
			long interval) {
		String objectId = session.getObjectId(selectorOfElement);
		Integer nodeId = session.getNodeId(selectorOfElement);
		CallFunctionOnResult functionResult = null;
		RemoteObject result = null;
		executeScript("function() { this.style.border='3px solid yellow'; }",
				selectorOfElement);
		sleep(interval);
		executeScript("function() { this.style.border=''; }", selectorOfElement);
	}

	protected Object executeScript(Session session, String script,
			String selectorOfElement) {
		if (!session.matches(selectorOfElement)) {
			return null;
		}
		String objectId = session.getObjectId(selectorOfElement);
		Integer nodeId = session.getNodeId(selectorOfElement);
		CallFunctionOnResult functionResult = null;
		RemoteObject result = null;
		Object value = null;
		try {
			functionResult = session.getCommand().getRuntime().callFunctionOn(script,
					objectId, null, null, null, null, null, null, nodeId, null);
			if (functionResult != null) {
				result = functionResult.getResult();
				if (result != null) {
					value = result.getValue();
					session.releaseObject(result.getObjectId());
				}
			}
		} catch (Exception e) {
			System.err.println("Exception (ignored): " + e.getMessage());
		}
		// System.err.println("value: " + value);
		return value;
	}
  • Wait until certain element is visible (similar to Selenium ExpectedConditions):
	protected boolean isVisible(String selectorOfElement) {
		return (boolean) (session.matches(selectorOfElement)
				&& (boolean) executeScript(
						"function() { return(this.offsetWidth > 0 || this.offsetHeight > 0); }",
						selectorOfElement));
	}

    session.navigate("https://www.google.com/gmail/about/#")
		session.waitUntil(o -> isVisible(selector), 1000, 100);
	protected void click(String selector) {
		executeScript(session, "function() { this.click(); }", selector);
	}
  • Compute the XPath / Css Selector or other DOM attributes by executing some Javascript (possibly recursively) in the broser:
cssSelectorOfElement = function(element) {
	if (!(element instanceof Element))
		return;
	var path = [];
	while (element.nodeType === Node.ELEMENT_NODE) {
		var selector = element.nodeName.toLowerCase();
		if (element.id) {
			if (element.id.indexOf('-') > -1) {
				selector += '[id="' + element.id + '"]';
			} else {
				selector += '#' + element.id;
			}
			path.unshift(selector);
			break;
		} else if (element.className) {
			selector += '.' + element.className.replace(/^\s+/,'').replace(/\s+$/,'').replace(/\s+/g, '.');
		} else {
			var element_sibling = element;
			var sibling_cnt = 1;
			while (element_sibling = element_sibling.previousElementSibling) {
				if (element_sibling.nodeName.toLowerCase() == selector)
					sibling_cnt++;
			}
			if (sibling_cnt != 1)
				selector += ':nth-of-type(' + sibling_cnt + ')';
		}
		path.unshift(selector);
		element = element.parentNode;
	}
	return path.join(' > ');
}
	protected String cssSelectorOfElement(String selectorOfElement) {
		session.evaluate(getScriptContent("cssSelectorOfElement.js"));
		return (String) executeScript("function() { return cssSelectorOfElement(this); }",
				selectorOfElement);
	}

	protected static String getScriptContent(String scriptName) {
		try {
			final InputStream stream = BaseTest.class.getClassLoader()
					.getResourceAsStream(scriptName);
			final byte[] bytes = new byte[stream.available()];
			stream.read(bytes);
			// System.err.println("Loaded:\n" + new String(bytes, "UTF-8"));
			return new String(bytes, "UTF-8");
		} catch (IOException e) {
			throw new RuntimeException("Cannot load file: " + scriptName);
		}
	}

		String xpath = "//*[@id='nav']//a[contains(@href, 'support.html')]";
		// Arrange
		session.waitUntil(o -> isVisible(xpath), 1000, 100);
		// Act
		highlight(xpath, 1000);
		String computedXPath = xpathOfElement(xpath);
		String computedCssSelector = cssSelectorOfElement(xpath);
		String computedText = textOfElement(xpath);
		// Assert
		Assert.assertEquals(computedXPath, "//nav[@id=\"nav\"]/ul/li[2]/a");
		Assert.assertEquals(computedCssSelector,
				"nav#nav > ul > li:nth-of-type(2) > a");
		Assert.assertEquals(computedText, "Support");
  • Finding the target element by applying findElement(s) to a certain element found earlier.
		// Arrange
		session.navigate("http://suvian.in/selenium/1.5married_radio.html");

		String formLocator = ".intro-header .container .row .col-lg-12 .intro-message form";
    // locate the form on the page
		session.waitUntil(o -> o.matches(formLocator), 1000, 100);
		assertThat(session.getObjectId(formLocator), notNullValue());
		highlight(formLocator, 1000);
		sleep(1000);

    // get the HTML of the form element
		elementContents = (String) executeScript(
				"function() { return this.outerHTML; }", formLocator);

		// Parse the HTML looking for "yes" or "no" - depends on desired married
		// status
		String label = "no";

		String line = new ArrayList<String>(
				Arrays.asList(elementContents.split("<br/?>"))).stream()
						.filter(o -> o.toLowerCase().indexOf(label) > -1).findFirst().get();
		Matcher matcher = Pattern.compile("value=\\\"([^\"]*)\\\"").matcher(line);
		String checkboxValue = null;
		if (matcher.find()) {
			checkboxValue = matcher.group(1);
			System.err.println("checkbox value = " + checkboxValue);
		} else {
			System.err.println("checkbox value not found");
		}
		String checkBoxElementId = null;

		// Act
    // combine the selectors
		String checkBoxElementSelector = String.format(
				"%s input[name='married'][value='%s']", formLocator, checkboxValue);
    checkBoxElementId = session.getObjectId(checkBoxElementSelector);
		// Assert the checkbox is found
		assertThat(checkBoxElementId, notNullValue());
		highlight(checkBoxElementSelector);
    // Act
		click(checkBoxElementSelector);
		// Assert that the checkbox gets into checked state (passes)
		assertThat(
				session.getObjectId(
						String.format("%s%s", checkBoxElementSelector, ":checked")),
				notNullValue());
		sleep(500);
		// Assert evaluate the checked semi attribute (fails, probably wrong sizzle)
		assertTrue(Boolean.parseBoolean(
				session.getAttribute(checkBoxElementSelector, ":checked")));
		sleep(500);
	}
  • Collect page timings (also possible to have the same at page element level):
// based on: https://github.com/addyosmani/timing.js/blob/master/timing.js
// NOTE: not computing timings.loadTime, timings.domReadyTime  etc.
(function(window) {
  'use strict';
  window.timing = window.timing || {
    getTimes: function(opt) {
      var performance = window.performance ||
        window.webkitPerformance || window.msPerformance ||
        window.mozPerformance;

      if (performance === undefined) {
        return '';
      }
      var timings = performance.timing || {};
        return JSON.stringify(timings);
    },

    getNetwork: function(opt) {
      var network = performance.getEntries() || {};
        return JSON.stringify(network);
    }
  }
})(typeof window !== 'undefined' ? window : {});

then

	private static String baseURL = "https://www.priceline.com/";
	private static int pageLoadTimeout = 5000;

	@Test(enabled = true)
	public void timingTest() throws Exception {

		session.navigate(baseURL);
		session.waitDocumentReady(pageLoadTimeout);

		session.evaluate(getScriptContent("timing.js"));

		String result = session.callFunction("window.timing.getTimes",
				String.class);
		if (result != null) {
			System.err.println("result: " + result);
		} else {
			throw new RuntimeException("result is null");
		}
	}

TODO

The following code fragments are yet unclear how to implement:

  • Alerts

NOTE:

The verison 3.0.4 of cd4pj.jar suffers from the following error:

java.lang.UnsatisfiedLinkError: io.webfolder.cdp.internal.winp.Native.getProcessId(I)I

See also

CDP .Net Usage with Powershell (NOTE: not verified, really)

Prerequisites

The following steps were done on 32 bit Windows 7 Vagrant Box

Install:

In Powershell 6 console load assemblies

$dllPath = "C:\Users\vagrant\Downloads\BaristaLabs.ChromeDevTools.Runtime.dll"
[System.Reflection.Assembly]::LoadFrom($dllPath)
$dllPath = "C:\Users\vagrant\Downloads\Tera.ChromeDevTools.dll"
$assembly = [System.Reflection.Assembly]::LoadFrom($dllPath);

$assemblyType = $assembly.GetTypes()[0]
$assemblyType.GetMethods() | select-object -property Name,Module

Start chrome with debugging port enabled

"c:\Program Files\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9222
  • the browser will be visible

Execrise creating sessions:

$chrome = new-object Tera.ChromeDevTools.Chrome(9222,$falsse)
$session = $chrome.CreateNewSession()

This is where one may get stuck with callback signatures.

All chrome commands should be asynchronuous, therefore a simple

$session.Navigate("http://www.google.com")

does not work, and $session.RunSynchronously() needs arguments.

Author

Serguei Kouzmine

cdp4j_tests's People

Contributors

sergueik avatar

Watchers

James Cloos avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.