Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SoftAssertions Implementation #1340

Merged
merged 4 commits into from
Aug 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.microsoft.playwright.assertions;

import com.microsoft.playwright.Page;
import com.microsoft.playwright.impl.PageAssertionsImpl;

import java.util.List;
import java.util.regex.Pattern;

class PageAssertionsImplProxy extends SoftAssertionsBase implements PageAssertions {
private final PageAssertionsImpl pageAssertions;

PageAssertionsImplProxy(Page page, List<Throwable> results) {
super(results);
this.pageAssertions = new PageAssertionsImpl(page);
}

private PageAssertionsImplProxy(List<Throwable> results, PageAssertionsImpl pageAssertions) {
super(results);
this.pageAssertions = pageAssertions;
}

@Override
public PageAssertions not() {
return new PageAssertionsImplProxy(super.results, (PageAssertionsImpl) pageAssertions.not());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just change the return type of PageAssertionsImpl.not() to PageAssertionsImpl.

}

@Override
public void hasTitle(String titleOrRegExp, HasTitleOptions options) {
assertAndCaptureResult(() -> pageAssertions.hasTitle(titleOrRegExp, options));
}

@Override
public void hasTitle(Pattern titleOrRegExp, HasTitleOptions options) {
assertAndCaptureResult(() -> pageAssertions.hasTitle(titleOrRegExp, options));
}

@Override
public void hasURL(String urlOrRegExp, HasURLOptions options) {
assertAndCaptureResult(() -> pageAssertions.hasURL(urlOrRegExp, options));
}

@Override
public void hasURL(Pattern urlOrRegExp, HasURLOptions options) {
assertAndCaptureResult(() -> pageAssertions.hasURL(urlOrRegExp, options));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.microsoft.playwright.assertions;

import com.microsoft.playwright.Page;
import com.microsoft.playwright.impl.PageAssertionsImpl;

import java.util.List;
import java.util.regex.Pattern;

class PageAssertionsProxy extends SoftAssertionsBase implements PageAssertions {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this class is subsumed by PageAssertionsImplProxy and can be deleted.

private final PageAssertionsImpl pageAssertions;

PageAssertionsProxy(Page page, List<Throwable> results) {
super(results);
this.pageAssertions = new PageAssertionsImpl(page);
}

@Override
public PageAssertions not() {
return pageAssertions.not();
}

@Override
public void hasTitle(String titleOrRegExp, HasTitleOptions options) {
assertAndCaptureResult(() -> pageAssertions.hasTitle(titleOrRegExp, options));
}

@Override
public void hasTitle(Pattern titleOrRegExp, HasTitleOptions options) {
assertAndCaptureResult(() -> pageAssertions.hasTitle(titleOrRegExp, options));
}

@Override
public void hasURL(String urlOrRegExp, HasURLOptions options) {
assertAndCaptureResult(() -> pageAssertions.hasURL(urlOrRegExp, options));
}

@Override
public void hasURL(Pattern urlOrRegExp, HasURLOptions options) {
assertAndCaptureResult(() -> pageAssertions.hasURL(urlOrRegExp, options));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.microsoft.playwright.assertions;

import com.microsoft.playwright.Page;

public interface SoftAssertions {
PageAssertions assertThat(Page page);

void assertAll();

static SoftAssertions create() {
return new SoftAssertionsImpl();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.microsoft.playwright.assertions;

import com.microsoft.playwright.PlaywrightException;
import org.opentest4j.AssertionFailedError;

import java.util.List;

class SoftAssertionsBase {
final List<Throwable> results;

public SoftAssertionsBase(List<Throwable> results) {
this.results = results;
}

void assertAndCaptureResult(Runnable assertion) {
try {
assertion.run();
} catch (AssertionFailedError | PlaywrightException failure) {
results.add(failure);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.microsoft.playwright.assertions;

import com.microsoft.playwright.Page;
import org.opentest4j.AssertionFailedError;

import java.util.ArrayList;
import java.util.List;

class SoftAssertionsImpl implements SoftAssertions {
final List<Throwable> results;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would use RuntimeException here and in PageAssertionsImplProxy and SoftAssertionsBase. We don't want to catch any Errors anyway and only catch unchecked exceptions in SoftAssertionsBase anyway.

Copy link
Contributor Author

@uchagani uchagani Aug 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yury-s, we catch AssertionFailedError which is an Error

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, didn't know that. Then Throwable is justified.


SoftAssertionsImpl() {
this.results = new ArrayList<>();
}

@Override
public PageAssertions assertThat(Page page) {
return new PageAssertionsImplProxy(page, results);
}

@Override
public void assertAll() {
if (!results.isEmpty()) {
throw new AssertionFailedError(getFormattedErrorMessage());
}
}

private String getFormattedErrorMessage() {
StringBuilder message = new StringBuilder();
message
.append(results.size())
.append(" assertion(s) failed:");

for (Throwable t : results) {
message.append("\n");
message.append("----------------------------------------\n");
message.append(t.getMessage());
}

return message.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package com.microsoft.playwright;

import com.microsoft.playwright.assertions.PageAssertions;
import com.microsoft.playwright.assertions.SoftAssertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.opentest4j.AssertionFailedError;

import java.util.regex.Pattern;

import static com.microsoft.playwright.Utils.assertFailureCount;
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class TestSoftPageAssertions extends TestBase {
private SoftAssertions softly;

@BeforeEach
void beforeEach() {
softly = SoftAssertions.create();
}

@Test
void hasUrlTextPass() {
page.navigate("data:text/html,<div>A</div>");
softly.assertThat(page).hasURL("data:text/html,<div>A</div>");
softly.assertAll();
assertFailureCount(softly, 0);
}

@Test
void hasURLTextFail() {
page.navigate("data:text/html,<div>B</div>");
softly.assertThat(page).hasURL("foo", new PageAssertions.HasURLOptions().setTimeout(1_000));
AssertionFailedError e = assertThrows(AssertionFailedError.class, () -> softly.assertAll());
assertTrue(e.getMessage().contains("1 assertion(s) failed"), e.getMessage());
assertTrue(e.getMessage().contains("Page URL expected to be"), e.getMessage());
assertFailureCount(softly, 1);
}

@Test
void shouldSupportHasUrlWithBaseUrl() {
try (BrowserContext context = browser.newContext(new Browser.NewContextOptions().setBaseURL(server.PREFIX))) {
Page page = context.newPage();
page.navigate(server.EMPTY_PAGE);
softly.assertThat(page).hasURL("/empty.html", new PageAssertions.HasURLOptions().setTimeout(1_000));
softly.assertAll();
assertFailureCount(softly, 0);
}
}

@Test
void notHasUrlText() {
page.navigate("data:text/html,<div>B</div>");
softly.assertThat(page).not().hasURL("about:blank", new PageAssertions.HasURLOptions().setTimeout(1000));
softly.assertAll();
assertFailureCount(softly, 0);
}

@Test
void hasURLRegexPass() {
page.navigate("data:text/html,<div>A</div>");
softly.assertThat(page).hasURL(Pattern.compile("text"));
softly.assertAll();
assertFailureCount(softly, 0);
}

@Test
void hasURLRegexFail() {
page.navigate(server.EMPTY_PAGE);
softly.assertThat(page).hasURL(Pattern.compile(".*foo.*"), new PageAssertions.HasURLOptions().setTimeout(1_000));
AssertionFailedError e = assertThrows(AssertionFailedError.class, () -> softly.assertAll());
assertTrue(e.getMessage().contains("1 assertion(s) failed"), e.getMessage());
assertTrue(e.getMessage().contains("Page URL expected to match regex"), e.getMessage());
assertFailureCount(softly, 1);
}

@Test
void notHasUrlRegEx() {
page.navigate("data:text/html,<div>B</div>");
softly.assertThat(page).not().hasURL(Pattern.compile("about"), new PageAssertions.HasURLOptions().setTimeout(1000));
softly.assertAll();
assertFailureCount(softly, 0);
}

@Test
void hasTitleTextPass() {
page.navigate(server.PREFIX + "/title.html");
softly.assertThat(page).hasTitle("Woof-Woof", new PageAssertions.HasTitleOptions().setTimeout(1_000));
softly.assertAll();
assertFailureCount(softly, 0);
}

@Test
void hasTitleTextNormalizeWhitespaces() {
page.setContent("<title> Foo Bar </title>");
softly.assertThat(page).hasTitle(" Foo Bar", new PageAssertions.HasTitleOptions().setTimeout(1_000));
softly.assertAll();
assertFailureCount(softly, 0);
}

@Test
void hasTitleTextFail() {
page.navigate(server.PREFIX + "/title.html");
softly.assertThat(page).hasTitle("foo", new PageAssertions.HasTitleOptions().setTimeout(1_000));
AssertionFailedError e = assertThrows(AssertionFailedError.class, () -> softly.assertAll());
assertTrue(e.getMessage().contains("1 assertion(s) failed"), e.getMessage());
assertTrue(e.getMessage().contains("Page title expected to be: foo\nReceived: Woof-Woof"), e.getMessage());
assertFailureCount(softly, 1);
}

@Test
void hasTitleRegexPass() {
page.navigate(server.PREFIX + "/title.html");
softly.assertThat(page).hasTitle(Pattern.compile("^.oof.+oof$"));
softly.assertAll();
assertFailureCount(softly, 0);
}

@Test
void hasTitleRegexFail() {
page.navigate(server.PREFIX + "/title.html");
softly.assertThat(page).hasTitle(Pattern.compile("^foo[AB]"), new PageAssertions.HasTitleOptions().setTimeout(1_000));
AssertionFailedError e = assertThrows(AssertionFailedError.class, () -> softly.assertAll());
assertTrue(e.getMessage().contains("1 assertion(s) failed"), e.getMessage());
assertTrue(e.getMessage().contains("Page title expected to match regex: ^foo[AB]\nReceived: Woof-Woof"), e.getMessage());
assertFailureCount(softly, 1);
}

@Test
void notHasTitleRegEx() {
page.navigate(server.PREFIX + "/title.html");
softly.assertThat(page).not().hasTitle(Pattern.compile("ab.ut"));
softly.assertAll();
assertFailureCount(softly, 0);
}

@Test
void hasTitleRegExCaseInsensitivePass() {
page.navigate(server.PREFIX + "/title.html");
softly.assertThat(page).hasTitle(Pattern.compile("woof-woof", Pattern.CASE_INSENSITIVE));
softly.assertAll();
assertFailureCount(softly, 0);
}
}
14 changes: 14 additions & 0 deletions playwright/src/test/java/com/microsoft/playwright/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.microsoft.playwright.assertions.SoftAssertions;

import java.io.*;
import java.lang.reflect.Field;
import java.net.ServerSocket;
import java.nio.file.Files;
import java.nio.file.Path;
Expand Down Expand Up @@ -214,4 +216,16 @@ static String generateDifferentOriginHostname(final Server server){
static String generateDifferentOriginPort(final Server server){
return server.PREFIX.replace(String.valueOf(server.PORT), String.valueOf(server.PORT+1));
}

static void assertFailureCount(SoftAssertions softAssertions, int expectedCount) {
try {
Class<? extends SoftAssertions> clazz = softAssertions.getClass();
Field resultsField = clazz.getDeclaredField("results");
resultsField.setAccessible(true);
List<Throwable> results = (List<Throwable>) resultsField.get(softAssertions);
assertEquals(results.size(), expectedCount);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
Loading