Skip to content

Commit

Permalink
#1028: Add Jsoup Assertions (#1035)
Browse files Browse the repository at this point in the history
* initial commit from jsoup assertions

* remove resource loader

* added some html matchers

* added some documentations
  • Loading branch information
faogustavo authored and sksamuel committed Oct 22, 2019
1 parent f3fa5b5 commit 458c641
Show file tree
Hide file tree
Showing 9 changed files with 341 additions and 0 deletions.
27 changes: 27 additions & 0 deletions doc/jsoup-matchers.md
@@ -0,0 +1,27 @@
Jsoup Matchers
==========

This page lists all current matchers in the KotlinTest jsoup matchers extension library. To use this library
you need to add `kotlintest-assertions-jsoup` to your build.

| Element | |
| --- | --- |
| `element.shouldHaveChildWithTag(tag)` | Asserts that the element has a child with the given tag |
| `element.shouldHaveText(text)` | Asserts that the element has the given text |
| `element.shouldHaveAttribute(name)` | Asserts that the element has an attribute with the given name |
| `element.shouldHaveAttributeValue(name, value)` | Asserts that the element have an attribute with the given value |

| Elements | |
| --- | --- |
| `elements.shouldBePresent()` | Asserts that the Elements object has some item |
| `elements.shouldBePresent(n)` | Asserts that the Elements object has N items |
| `elements.shouldBePresent(n)` | Asserts that the Elements object has N items |

| HTML | |
| --- | --- |
| `element.shouldHaveId(id)` | Asserts that the element has an attribute id with the given value |
| `element.shouldHaveClass(class)` | Asserts that the element has the specified class |
| `element.shouldHaveSrc(src)` | Asserts that the element has an attribute src with the given value |
| `element.shouldHaveHref(href)` | Asserts that the element has an attribute href with the given value |
| `element.shouldHaveElementWithId(id)` | Asserts that the element has a child with the given id |
| `element.shouldHaveChildWithClass(id)` | Asserts that the element has a child with the given class |
Empty file.
54 changes: 54 additions & 0 deletions kotest-assertions/kotest-assertions-jsoup/build.gradle
@@ -0,0 +1,54 @@
plugins {
id 'java'
id 'org.jetbrains.kotlin.multiplatform'
id 'java-library'
}

repositories {
mavenCentral()
}

kotlin {

jvm()

sourceSets {

jvmMain {
dependencies {
implementation project(":kotest-assertions")
implementation "org.jsoup:jsoup:1.11.2"
}
}

jvmTest {
dependencies {
implementation 'io.kotlintest:kotlintest-runner-junit5:3.3.2'
implementation project(':kotest-runner:kotest-runner-junit5')
}
}
}
}

compileKotlinJvm {
kotlinOptions {
freeCompilerArgs += '-Xuse-experimental=kotlin.Experimental'
}
}

jvmTest {
useJUnitPlatform()

// show standard out and standard error of the test JVM(s) on the console
testLogging.showStandardStreams = true

// Always run tests, even when nothing changed.
dependsOn 'cleanTest'

testLogging {
events "FAILED", "SKIPPED", "STANDARD_OUT", "STANDARD_ERROR"
exceptionFormat = 'full'
}
}

apply from: '../../publish.gradle'
@@ -0,0 +1,70 @@
package io.kotest.assertions.jsoup

import io.kotest.Matcher
import io.kotest.MatcherResult
import io.kotest.should
import io.kotest.shouldNot
import org.jsoup.nodes.Element
import org.jsoup.select.Elements

fun Elements.shouldBePresent() = this should bePresent()
fun Elements.shouldNotBePresent() = this shouldNot bePresent()
fun bePresent() = object : Matcher<Elements> {
override fun test(value: Elements) = MatcherResult(
value.isNotEmpty(),
"Element should be present",
"Element should not be present"
)
}

infix fun Elements.shouldBePresent(times: Int) = this should bePresent(times)
infix fun Elements.shouldNotBePresent(times: Int) = this shouldNot bePresent(times)
fun bePresent(times: Int) = object : Matcher<Elements> {
override fun test(value: Elements) = MatcherResult(
value.size == times,
"Element should be present $times times",
"Element should not be present $times times"
)
}

infix fun Element.shouldHaveChildWithTag(tag: String) = this should haveChildWithTag(tag)
infix fun Element.shouldNotHaveChildWithTag(tag: String) = this shouldNot haveChildWithTag(tag)
fun haveChildWithTag(tag: String) = object : Matcher<Element> {
override fun test(value: Element) = MatcherResult(
value.getElementsByTag(tag).isNotEmpty(),
"Document should have at least one child with tag $tag",
"Document should not have child with tag $tag"
)
}

infix fun Element.shouldHaveText(expectedText: String) = this should haveText(expectedText)
infix fun Element.shouldNotHaveText(expectedText: String) = this shouldNot haveText(expectedText)
fun haveText(expectedText: String) = object : Matcher<Element> {
override fun test(value: Element) = MatcherResult(
value.text() == expectedText,
"Element ${value.tagName()} should have text $expectedText. But instead was $expectedText",
"Element ${value.tagName()} should not have text $expectedText"
)
}

infix fun Element.shouldHaveAttribute(attrName: String) = this should haveAttribute(attrName)
infix fun Element.shouldNotHaveAttribute(attrName: String) = this shouldNot haveAttribute(attrName)
fun haveAttribute(attrName: String) = object : Matcher<Element> {
override fun test(value: Element) = MatcherResult(
value.hasAttr(attrName),
"Element ${value.tagName()} should have attribute $attrName.",
"Element ${value.tagName()} should not have attribute $attrName."
)
}

fun Element.shouldHaveAttributeValue(attr: String, expectedValue: String) =
this should haveAttrValue(attr, expectedValue)
fun Element.shouldNotHaveAttributeValue(attr: String, expectedValue: String) =
this shouldNot haveAttrValue(attr, expectedValue)
fun haveAttrValue(attr: String, expectedValue: String) = object : Matcher<Element> {
override fun test(value: Element) = MatcherResult(
value.attr(attr) == expectedValue,
"Element should have attribute $attr with value $expectedValue",
"Element should not have attribute $attr with value $expectedValue"
)
}
@@ -0,0 +1,53 @@
package io.kotest.assertions.jsoup

import io.kotest.Matcher
import io.kotest.MatcherResult
import io.kotest.should
import io.kotest.shouldNot
import org.jsoup.nodes.Element

infix fun Element.shouldHaveId(id: String) = this should haveId(id)
infix fun Element.shouldNotHaveId(id: String) = this shouldNot haveId(id)
fun haveId(id: String) = object : Matcher<Element> {
override fun test(value: Element) = MatcherResult(
value.id() == id,
"Element ${value.tagName()} should have id $id.",
"Element ${value.tagName()} should not have id $id."
)
}

infix fun Element.shouldHaveClass(className: String) = this should haveClass(className)
infix fun Element.shouldNotHaveClass(className: String) = this shouldNot haveClass(className)
fun haveClass(className: String) = object : Matcher<Element> {
override fun test(value: Element) = MatcherResult(
value.hasClass(className),
"Element ${value.tagName()} should have class $className.",
"Element ${value.tagName()} should not have text $className."
)
}

infix fun Element.shouldHaveSrc(src: String) = this should haveAttrValue("src", src)
infix fun Element.shouldNotHaveSrc(src: String) = this shouldNot haveAttrValue("src", src)

infix fun Element.shouldHaveHref(src: String) = this should haveAttrValue("href", src)
infix fun Element.shouldNotHaveHref(src: String) = this shouldNot haveAttrValue("href", src)

infix fun Element.shouldHaveElementWithId(id: String) = this should haveElementWithId(id)
infix fun Element.shouldNotHaveElementWithId(id: String) = this shouldNot haveElementWithId(id)
fun haveElementWithId(id: String) = object : Matcher<Element> {
override fun test(value: Element) = MatcherResult(
value.getElementById(id) != null,
"Element should have a child with id $id",
"Element should not have a child with id $id"
)
}

infix fun Element.shouldHaveChildWithClass(clazz: String) = this should haveChildWithClass(clazz)
infix fun Element.shouldNotHaveChildWithClass(clazz: String) = this shouldNot haveChildWithClass(clazz)
fun haveChildWithClass(clazz: String) = object : Matcher<Element> {
override fun test(value: Element) = MatcherResult(
value.getElementsByClass(clazz).isNotEmpty(),
"Element should have at least one child with class $clazz",
"Element should not have child with class $clazz"
)
}
@@ -0,0 +1,61 @@
package io.kotest.assertions.jsoup

import io.kotest.specs.FreeSpec
import org.jsoup.Jsoup

class ElementMatchersTest : FreeSpec() {
init {
val html = javaClass.classLoader.getResourceAsStream("example.html").bufferedReader().use { it.readText() }
val root = Jsoup.parse(html)
"should" - {
"bePresent" {
val data = root.getElementsByTag("p")
data.shouldBePresent()
}
"bePresent N Times" {
val data = root.getElementsByTag("p")
data shouldBePresent 2
}
"haveText" {
val data = root.getElementsByTag("h1").first()
data shouldHaveText "i'm the headline"
}
"haveAttribute" {
val data = root.getElementsByTag("html").first()
data shouldHaveAttribute "lang"
}
"haveChildWithTag" {
root shouldHaveChildWithTag "body"
}
"haveAttrValue" {
val data = root.getElementsByTag("html").first()
data.shouldHaveAttributeValue("lang", "en")
}
}
"shouldNot" - {
"bePresent" {
val data = root.getElementsByTag("script")
data.shouldNotBePresent()
}
"bePresent N Times" {
val data = root.getElementsByTag("body")
data shouldNotBePresent 2
}
"haveText" {
val data = root.getElementsByTag("h1").first()
data shouldNotHaveText "i'm not the headline"
}
"haveAttribute" {
val data = root.getElementsByTag("h1").first()
data shouldNotHaveAttribute "lang"
}
"haveChildWithTag" {
root shouldNotHaveChildWithTag "foot"
}
"haveAttrValue" {
val data = root.getElementsByTag("html").first()
data.shouldNotHaveAttributeValue("lang", "es")
}
}
}
}
@@ -0,0 +1,59 @@
package io.kotest.assertions.jsoup

import io.kotest.specs.FreeSpec
import org.jsoup.Jsoup

class HtmlMatchersTest : FreeSpec() {
init {
val html = javaClass.classLoader.getResourceAsStream("example.html").bufferedReader().use { it.readText() }
val root = Jsoup.parse(html)
"should" - {
"haveId" {
val data = root.getElementsByTag("header").first()
data shouldHaveId "abc"
}
"haveClass" {
val data = root.getElementsByTag("body").first()
data shouldHaveClass "someClass"
}
"haveSrc" {
val data = root.getElementsByTag("img").first()
data shouldHaveSrc "http://image.url/test"
}
"haveHref" {
val data = root.getElementsByTag("a").first()
data shouldHaveHref "some.link"
}
"haveElementWithId" {
root shouldHaveElementWithId "abc"
}
"haveChildWithClass" {
root shouldHaveChildWithClass "someClass"
}
}
"shouldNot" - {
"haveId" {
val data = root.getElementsByTag("header").first()
data shouldNotHaveId "a1b2c3"
}
"haveClass" {
val data = root.getElementsByTag("body").first()
data shouldNotHaveClass "someOtherClass"
}
"haveSrc" {
val data = root.getElementsByTag("img").first()
data shouldNotHaveSrc "http://some.other-image.url/test"
}
"haveHref" {
val data = root.getElementsByTag("a").first()
data shouldNotHaveHref "http://some.other-image.url/test"
}
"haveElementWithId" {
root shouldNotHaveElementWithId "def"
}
"haveChildWithClass" {
root shouldNotHaveChildWithClass "someOtherClass"
}
}
}
}
@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>i'm the title</title>
</head>
<body class="someClass">
i'm the body
<header id="abc" class="someHeaderClass">
<h1>i'm the headline</h1>
</header>
<p>i'm a paragraph</p>
<p>i'm a second paragraph</p>
<img src="http://image.url/test" />
<a href="some.link">Click Me</a>
</body>
</html>
1 change: 1 addition & 0 deletions settings.gradle
Expand Up @@ -5,6 +5,7 @@ include 'kotest-core',
'kotest-assertions:kotest-assertions-arrow',
'kotest-assertions:kotest-assertions-json',
'kotest-assertions:kotest-assertions-ktor',
'kotest-assertions:kotest-assertions-jsoup',
'kotest-runner:kotest-runner-jvm',
'kotest-runner:kotest-runner-console',
'kotest-runner:kotest-runner-js',
Expand Down

0 comments on commit 458c641

Please sign in to comment.