Skip to content

Commit

Permalink
Add remark slides for meetup
Browse files Browse the repository at this point in the history
  • Loading branch information
vKito committed Mar 20, 2023
1 parent 44f7d47 commit 0776405
Show file tree
Hide file tree
Showing 5 changed files with 301 additions and 9 deletions.
4 changes: 2 additions & 2 deletions .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"isRoot": true,
"tools": {
"fable": {
"version": "4.0.0-theta-018",
"version": "4.0.1",
"commands": [
"fable"
]
Expand All @@ -15,4 +15,4 @@
]
}
}
}
}
11 changes: 10 additions & 1 deletion App.Test/Main.fs
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@ type PlaywrightTests(outputHelper: ITestOutputHelper) =
let logger msg = outputHelper.WriteLine(msg)
let playwright = Playwright.CreateAsync().GetAwaiter().GetResult()
let isHeadless = false
let slowMoAmount = 0f

[<Fact>]
member this.``Loads home page``() = task {
let launchOptions = BrowserTypeLaunchOptions()
launchOptions.Headless <- isHeadless
launchOptions.SlowMo <- slowMoAmount

let! browser = playwright.Firefox.LaunchAsync(launchOptions)
let! context = browser.NewContextAsync(BrowserNewContextOptions(IgnoreHTTPSErrors = true))
Expand Down Expand Up @@ -69,6 +71,7 @@ type PlaywrightTests(outputHelper: ITestOutputHelper) =
member this.``Filters by tag``() = task {
let launchOptions = BrowserTypeLaunchOptions()
launchOptions.Headless <- isHeadless
launchOptions.SlowMo <- slowMoAmount

let! browser = playwright.Firefox.LaunchAsync(launchOptions)
let! context = browser.NewContextAsync(BrowserNewContextOptions(IgnoreHTTPSErrors = true))
Expand Down Expand Up @@ -102,6 +105,7 @@ type PlaywrightTests(outputHelper: ITestOutputHelper) =
member this.``Profile page shows author name in banner``() = task {
let launchOptions = BrowserTypeLaunchOptions()
launchOptions.Headless <- isHeadless
launchOptions.SlowMo <- slowMoAmount

let! browser = playwright.Firefox.LaunchAsync(launchOptions)
let! context = browser.NewContextAsync(BrowserNewContextOptions(IgnoreHTTPSErrors = true))
Expand All @@ -123,6 +127,7 @@ type PlaywrightTests(outputHelper: ITestOutputHelper) =
member this.``Profile page shows article title``() = task {
let launchOptions = BrowserTypeLaunchOptions()
launchOptions.Headless <- isHeadless
launchOptions.SlowMo <- slowMoAmount

let! browser = playwright.Firefox.LaunchAsync(launchOptions)
let! context = browser.NewContextAsync(BrowserNewContextOptions(IgnoreHTTPSErrors = true))
Expand Down Expand Up @@ -150,6 +155,7 @@ type PlaywrightTests(outputHelper: ITestOutputHelper) =
member this.``Home link in nav is clickable``() = task {
let launchOptions = BrowserTypeLaunchOptions()
launchOptions.Headless <- isHeadless
launchOptions.SlowMo <- slowMoAmount

let! browser = playwright.Firefox.LaunchAsync(launchOptions)
let! context = browser.NewContextAsync(BrowserNewContextOptions(IgnoreHTTPSErrors = true))
Expand Down Expand Up @@ -177,6 +183,7 @@ type PlaywrightTests(outputHelper: ITestOutputHelper) =
member this.``Can sign up``() = task {
let launchOptions = BrowserTypeLaunchOptions()
launchOptions.Headless <- isHeadless
launchOptions.SlowMo <- slowMoAmount

let! browser = playwright.Firefox.LaunchAsync(launchOptions)
let! context = browser.NewContextAsync(BrowserNewContextOptions(IgnoreHTTPSErrors = true))
Expand Down Expand Up @@ -225,6 +232,7 @@ type PlaywrightTests(outputHelper: ITestOutputHelper) =
member this.``Can publish new article``() = task {
let launchOptions = BrowserTypeLaunchOptions()
launchOptions.Headless <- isHeadless
launchOptions.SlowMo <- slowMoAmount

let! browser = playwright.Firefox.LaunchAsync(launchOptions)
let! context = browser.NewContextAsync(BrowserNewContextOptions(IgnoreHTTPSErrors = true))
Expand Down Expand Up @@ -301,6 +309,7 @@ type PlaywrightTests(outputHelper: ITestOutputHelper) =
member this.``Can favorite an article``() = task {
let launchOptions = BrowserTypeLaunchOptions()
launchOptions.Headless <- isHeadless
launchOptions.SlowMo <- slowMoAmount

let! browser = playwright.Firefox.LaunchAsync(launchOptions)
let! context = browser.NewContextAsync(BrowserNewContextOptions(IgnoreHTTPSErrors = true))
Expand Down Expand Up @@ -371,7 +380,7 @@ type PlaywrightTests(outputHelper: ITestOutputHelper) =
member this.``Run Scrutiny Test``() = task {
let launchOptions = BrowserTypeLaunchOptions()
launchOptions.Headless <- isHeadless
//launchOptions.SlowMo <- 500f
launchOptions.SlowMo <- slowMoAmount

let! browser = playwright.Firefox.LaunchAsync(launchOptions)
let! context = browser.NewContextAsync(BrowserNewContextOptions(IgnoreHTTPSErrors = true))
Expand Down
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ An attempt at building a [RealWorld](https://github.com/gothinkster/realworld) a

The following tools are required:

* .Net 5.0
* Node.js 14.x
* PNPM 6.x
* .Net 7.0
* Node.js 16.x
* PNPM 7.x

For first time setup, run the following

Expand Down Expand Up @@ -46,4 +46,3 @@ To build for prod:
* [ ] Error handling within the application itself
* [ ] Commenting system
* [ ] Icons for header menu items when logged in
* [ ] Routing (URL) is all sorts of janky
284 changes: 284 additions & 0 deletions slides.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
<!DOCTYPE html>
<html>
<head>
<title>Title</title>
<meta charset="utf-8">
<style>
h1, h2, h3 {
font-weight: normal;
}

li {
font-size: 24pt;
margin-bottom: 2rem;
}

.remark-code, .remark-inline-code {
font-family: 'Ubuntu Mono';
}
</style>
</head>
<body>
<textarea id="source">

class: center, middle

# Scrutiny

## Uncover edge cases by bringing Property-Based Tests to the UI

---

# Agenda

1. UI Testing is terrible
2. Property-based tests
3. User's journey
4. How to better

---

# UI Testing is terrible

* Tedious to write tests
* Flaky runs
* "Test Modeling"

???
Actually writing tests is incredibly tedious. Difficult to use the correct methods for finding the wanted DOM elements

Test runs suffer from "flakiness", but this is more to do with the actual tests themselves,
not an inherent property of UI tests

Database is one giant state

Scrutiny won't help with any of these

---

# Writing tests is tedious

* Lot's of duplicated selectors
* Many actions need to be repeated
* The only path that is tested is what the developer thinks of

???
Show code examples of the basic tests:

* Can sign up
* Can publish new article
* Can favorite an article

Mention that similar problems exist with "normal" unit/integration tests. It just happens to be less of a problem because things move much faster

---

# Property based tests

* Automagically generates input values to your SUT
* Tests "properties" of your system
* Helps uncover edge cases that you didn't think of during development

---

# Example property test

Function to test
```fsharp
let rev list =
list |> List.rev;;

rev [1; 2; 3; 4];;
// [4; 3; 2; 1]

```

Property based test
```fsharp
// Uses FsCheck
[<Property>]
let testRev inputList =
let property = inputList |> rev |> rev
Assert.sequenceEquals inputList property
```

`inputList` is randomly generated for (default) 100 different lists
---

# User's journey

* How do you expect your user to use your UI
* Users are unpredictable
* Strange interactions occur

---

# Usual UI tests

User favorites an article
<div class="mermaid">
flowchart LR
login[User logs in] --> navigate
findArticle[Finds article to favorite] --- navigate[Navigates to home page]
findArticle --> feed[Clicks favorite button]
feed[User navigates to Your Feed] --> verify[User verifies favorite article shows up in own feed]
</div>

User write new article
<div class="mermaid">
flowchart LR
login[User logs in] --> navigate[Navigates to home page]
newArticle[Navigates to new article page] --- navigate[Navigates to home page]
newArticle --> feed[Writes new article]
feed[User navigates to new article] --> verify[User verifies new article is published]
</div>

---

# Scrutiny UI Tests

User favorites an article and then writes a new article
<div class="mermaid">
flowchart LR
login[User logs in] --> navigate
findArticle[Finds article to favorite] --- navigate[Navigates to home page]
findArticle --> feed[Clicks favorite button]
feed[User navigates to Your Feed] --> verify
newArticle[Navigates to new article page] --- verify[User verifies favorite article shows up in own feed]
newArticle --> feed2[Writes new article]
feed2[User navigates to new article] --> verify2[User verifies new article is published]
</div>

???

Show crappy test site after this

---

# Describe your UI as a state machine

<div class="mermaid">
stateDiagram
direction LR

loggedincomment: Logged In Comment
loggedinhome: Logged in Home
comment: Comment
signin: Sign In
home: Home

loggedincomment --> loggedinhome
loggedinhome --> home
loggedinhome --> loggedincomment
comment --> signin
comment --> home
signin --> loggedinhome
signin --> home
home --> signin
home --> comment
</div>

---

# State definition

```fsharp
page {
name "Name of page"
onEnter (fun ls ->
// Do something when first entering state
Assert.equal headerText expectedHeader
)
onExit (fun _ ->
// Do something when leaving state
printfn "Exiting comment"
)
transition {
via (fun ls -> click ls.HomeLink)
destination home
}
transition {
via (fun _ -> click "#signin")
destination signIn
}
action {
fn (fun _ -> () (*do something on the page*))
}
action {
isExit
fn (fun _ -> () (*final action to perform before exiting the test*))
}
}
```

???
Show crappy web site scrutiny test

---

# For the C# people

```csharp
[PageState]
public class LoggedInComment
{
[OnEnter]
public void OnEnter() { }

[Action]
public async Task WriteComments() { }

[Action(IsExit = true)]
public async Task ExitAction() { }

[ExitAction]
public async Task ExitAction() { }

[TransitionTo(nameof(AnotherState))]
[DependantAction(nameof(WriteComments))] // Optioanlly run the WriteComments action before executing this transition
public void TransitionToAnotherState() { }
}
```

???
Show crappy web site scrutiny test C# usage example

---

# Resources

### Scrutiny

* https://codeberg.org/CubeOfShame/Scrutiny
* https://github.com/kaeedo/Scrutiny

### Subtle Conduit

* https://codeberg.org/CubeOfShame/SubtleConduit
* https://github.com/kaeedo/SubtleConduit

</textarea>
<script src="https://remarkjs.com/downloads/remark-latest.min.js"></script>
<script type="module">
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs';

mermaid.initialize({
startOnLoad: false,
cloneCssStyles: false
});

function initMermaid(s) {
const diagrams = document.querySelectorAll('.mermaid');
for (let i = 0; i < diagrams.length; i++) {
if (diagrams[i].offsetWidth > 0) {
mermaid.init(undefined, diagrams[i]);
}
}
}
const slideshow = remark.create();

slideshow.on('afterShowSlide', initMermaid);
initMermaid(slideshow.getSlides()[slideshow.getCurrentSlideIndex()]);
</script>
</body>
</html>
Loading

0 comments on commit 0776405

Please sign in to comment.