Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
58 changes: 58 additions & 0 deletions inst/apps/309-flexdashboard-tabs-navs/index.Rmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
---
title: 309
output:
flexdashboard::flex_dashboard:
orientation: row
runtime: shiny
---

Page 1 {data-test-id="Page 1"}
===================================

## Box 1-1 {.tabset data-test-id="Box 1-1"}

### Tab 1-1a {data-test-id="Tab 1-1a"}

Tab content, page 1, box 1, tab a

### Tab 1-1b {data-test-id="Tab 1-1b"}

Tab content, page 1, box 1, tab b

## Box 1-2 {.tabset data-test-id="Box 1-2"}

### Tab 1-2a {data-test-id="Tab 1-2a"}

Tab content, page 1, box 2, tab a

### Tab 1-2b {data-test-id="Tab 1-2b"}

Tab content, page 1, box 2, tab b


Page 2 {data-navmenu="Other" data-test-id="Page 2"}
===================================

## Row

### Box 2-1 {data-test-id="Box 2-1"}

Content, page 2, box 1

### Box 2-2 {data-test-id="Box 2-2"}

Content, page 2, box 2


Page 3 {data-navmenu="Other" data-test-id="Page 3"}
===================================

## Row

### Box 3-1 {data-test-id="Box 3-1"}

Content, page 3, box 1

### Box 3-2 {data-test-id="Box 3-2"}

Content, page 3, box 2
1 change: 1 addition & 0 deletions inst/apps/309-flexdashboard-tabs-navs/tests/testthat.R
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
shinytest2::test_app()
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Load application support files into testing environment
shinytest2::load_app_env()
107 changes: 107 additions & 0 deletions inst/apps/309-flexdashboard-tabs-navs/tests/testthat/test-shinytest2.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
library(shinytest2)

is_element_visible <- function(selector) {
sprintf(
"(function() {
const br = document.querySelector('%s').getBoundingClientRect()
return br.width > 0 && br.height > 0 && (br.x > 0 || br.y > 0)
})()",
selector
)
}

is_test_element_visible <- function(test_id) {
is_element_visible(sprintf('[data-test-id="%s"]', test_id))
}

expect_test_element_visible <- function(app, test_id) {
expect_true(app$get_js(is_test_element_visible(!!test_id)))
return(invisible(app))
}

expect_test_element_hidden <- function(app, test_id) {
expect_false(app$get_js(is_test_element_visible(!!test_id)))
Copy link
Contributor

Choose a reason for hiding this comment

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

Since these tests are essentially purely client-side, it'd probably faster/better to use {shinyjster} instead of {shinytest2}, for example:

jster <- shinyjster::shinyjster_js(
"
var jst = jster(0);
jst.add(Jster.shiny.waitUntilStable);
jst.add(function() {
var toggle = $('.navbar-toggle:visible');
var nav = $('.navbar-collapse:visible');
Jster.assert.isEqual(toggle.length, 1, 'Failed to find collapsible menu, does the window need to be resized?');
Jster.assert.isEqual(nav.length, 0, 'The collapsible navbar should not be visible by default');
toggle.click();
});
// wait for nav to open
jst.add(function(done) {
var wait = function() {
if ($('.navbar-collapse:visible').length > 0) {
done();
} else {
setTimeout(wait, 5);
}
}
wait();
});
jst.add(function() {
Jster.assert.isEqual(
$('.navbar-collapse:visible').length, 1,
'Clicking the navbar toggle should make the navbar appear.'
);
});
jst.test();
"
)

Copy link
Member Author

Choose a reason for hiding this comment

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

@schloerke Is shinyjster designed to facilitate running and testing R Markdown Shiny apps using runtime: shiny? (I couldn't find anything in the docs.)

It seems like the biggest advantage of using shinyjster would be to test in Chrome, Edge, and Firefox. From looking at the logs of previous runs, though, and unless I'm misunderstanding them, I would expect switching to shinyjster to increase the test time for these tests. Right now, they take ~7 seconds locally on my M1 for all three Bootstrap variants. With shinyjster we'd be adding two more browsers into the matrix.

Copy link
Contributor

Choose a reason for hiding this comment

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

{shinyjster} can run inside Rmd docs. Ex:

```{r}
## `{shinyjster}` note:
# From https://github.com/rstudio/shiny/issues/3780, we must delay the underlying initial
# call to `Shiny.setInputValue("jster_initialized", true)` due to changes in https://github.com/rstudio/shiny/pull/3666.
# Current stance is that https://github.com/rstudio/shiny/issues/3780 will not be resolved, so we must make a work around.
# This is done by delaying the initial call to `Shiny.setInputValue("jster_initialized", true)`
# by using a dynamic UI that is invalidated on the first draw, and then actually rendered on the second draw.
renderUI({
shinyjster::shinyjster_js(
"
var jst = jster();
jst.add(Jster.shiny.waitUntilStable);
jst.add(function(done) {
var wait = function() {
var txt = $('#status').get(0).textContent;
if (
typeof txt == 'string' &&
txt.length > 0 &&
(txt.match(new RegExp('Pass|Fail')) ?? '').length > 0
) {
done();
return;
}
setTimeout(wait, 100);
}
wait();
})
jst.add(function() {
Jster.assert.isEqual(
$('#status').text().trim(),
'Pass'
)
})
jst.test();
"
)
})
shinyjster::shinyjster_server(input, output)
```

Yes, it's reasonable that the time would be increased, but it is not by a large factor. I'd be comfortable using shinyjster.

return(invisible(app))
}

for (bs_version in 3:5) {
test_that(paste0("309-flexdashboard-tabs-navs with BS", bs_version), {
app <- AppDriver$new(
name = "309-flexdashboard-tabs-navs",
seed = 62868,
height = 1292,
width = 798,
view = interactive(),
render_args = list(output_options = list(theme = list(version = bs_version)))
)

app$wait_for_idle()
app$wait_for_js(is_test_element_visible("Page 1"))

expect_test_element_visible(app, "Page 1")
expect_test_element_visible(app, "Tab 1-1a")
expect_test_element_visible(app, "Tab 1-2a")
for (tab in c("Tab 1-1a", "Tab 1-2a")) {
expect_test_element_visible(app, tab)
}

expect_test_element_hidden(app, "Page 2")
expect_test_element_hidden(app, "Page 3")
for (box in c("Box 2-1", "Box 2-2", "Box 3-1", "Box 3-2")) {
expect_test_element_hidden(app, box)
}
for (tab in c("Tab 1-1b", "Tab 1-2b")) {
expect_test_element_hidden(app, tab)
}

# activate second tabs and check that visibility has switched
app$
click(selector = '[data-test-id="Page 1"] .nav-tabs [href$="tab-1-1b"]')$
wait_for_js(is_test_element_visible("Tab 1-1b"))

app$
click(selector = '[data-test-id="Page 1"] .nav-tabs [href$="tab-1-2b"]')$
wait_for_js(is_test_element_visible("Tab 1-2b"))

for (tab in c("Tab 1-1a", "Tab 1-2a")) {
expect_test_element_hidden(app, tab)
}
for (tab in c("Tab 1-1b", "Tab 1-2b")) {
expect_test_element_visible(app, tab)
}

# Go to page 2
app$
click(selector = ".nav .dropdown .dropdown-toggle")$
wait_for_js(is_element_visible(".nav .dropdown .dropdown-menu"))$
click(selector = '.nav .dropdown-item[href$="page-2"]')$
wait_for_js(is_test_element_visible("Page 2"))

expect_test_element_visible(app, "Page 2")
expect_test_element_visible(app, "Box 2-1")
expect_test_element_visible(app, "Box 2-2")

expect_test_element_hidden(app, "Page 1")
expect_test_element_hidden(app, "Page 3")
for (box in c("Box 1-1", "Box 1-2", "Box 3-1", "Box 3-2")) {
expect_test_element_hidden(app, box)
}

# Go to page 3
app$
click(selector = ".nav .dropdown .dropdown-toggle")$
wait_for_js(is_element_visible(".nav .dropdown .dropdown-menu"))$
click(selector = '.nav .dropdown-item[href$="page-3"]')$
wait_for_js(is_test_element_visible("Page 3"))

expect_test_element_visible(app, "Page 3")
expect_test_element_visible(app, "Box 3-1")
expect_test_element_visible(app, "Box 3-2")

expect_test_element_hidden(app, "Page 1")
expect_test_element_hidden(app, "Page 2")
for (box in c("Box 1-1", "Box 1-2", "Box 2-1", "Box 2-2")) {
expect_test_element_hidden(app, box)
}
})
}