Skip to content

Commit be23d31

Browse files
authored
Add unit tests (#30)
* Add unit tests * Add .get_htmlwidget * Fix test * Bump workflow versions
1 parent e63c0fb commit be23d31

File tree

4 files changed

+163
-11
lines changed

4 files changed

+163
-11
lines changed

.github/workflows/deploy.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
env:
1515
cache-version: 5
1616
steps:
17-
- uses: actions/checkout@v2
17+
- uses: actions/checkout@v4
1818
- name: Set up libraries for Ubuntu
1919
run: |
2020
sudo apt-get update
@@ -36,7 +36,7 @@ jobs:
3636
shell: Rscript {0}
3737
- name: Cache dependencies
3838
id: cache-deps
39-
uses: actions/cache@v2
39+
uses: actions/cache@v4
4040
with:
4141
path: ${{ env.R_LIBS_USER }}/*
4242
key: ${{ hashFiles('DESCRIPTION') }}-${{ steps.get-version.outputs.os-version }}-${{ steps.get-version.outputs.r-version }}-${{ env.cache-version }}-deps
@@ -69,7 +69,7 @@ jobs:
6969
run: |
7070
Rscript -e 'devtools::document(); pkgdown::build_site(new_process = FALSE)'
7171
touch docs/.nojekyll
72-
- uses: actions/upload-pages-artifact@v1
72+
- uses: actions/upload-pages-artifact@v3
7373
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
7474
with:
7575
path: ./docs
@@ -84,4 +84,4 @@ jobs:
8484
- name: Deploy to GitHub Pages
8585
id: deployment
8686
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
87-
uses: actions/deploy-pages@v1
87+
uses: actions/deploy-pages@v4

R/widget.R

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -247,33 +247,46 @@ AnyHtmlWidget <- R6::R6Class("AnyHtmlWidget",
247247
},
248248
#' @description
249249
#' Render the widget.
250-
render = function() {
250+
render = function(return_widget = FALSE) {
251251
if(private$.mode == "static") {
252-
invoke_static(self)
252+
invoke_static(self, return_widget = return_widget)
253253
} else if(private$.mode == "gadget") {
254-
invoke_gadget(self)
254+
invoke_gadget(self, return_widget = return_widget)
255255
} else if(private$.mode == "dynamic") {
256-
invoke_dynamic(self)
256+
invoke_dynamic(self, return_widget = return_widget)
257257
} else {
258258
stop("render is meant for use with static, gadget, and dynamic modes")
259259
}
260+
},
261+
#' @description
262+
#' Return the htmlwidget.
263+
#' Only works in "static" or "dynamic" mode.
264+
.get_htmlwidget = function() {
265+
if(private$.mode == "static" || private$.mode == "dynamic") {
266+
self$render(return_widget = TRUE)
267+
} else {
268+
stop(".get_htmlwidget is meant for use with static and dynamic modes")
269+
}
260270
}
261271
)
262272
)
263273

264274
#' @keywords internal
265-
invoke_static <- function(w) {
275+
invoke_static <- function(w, return_widget = FALSE) {
266276
w <- the_anyhtmlwidget(
267277
esm = w$.get_esm(),
268278
values = w$.get_values(),
269279
width = w$.get_width(),
270280
height = w$.get_height()
271281
)
282+
if(return_widget) {
283+
return(w)
284+
}
272285
print(w)
273286
}
274287

275288
#' @keywords internal
276-
invoke_dynamic <- function(w) {
289+
invoke_dynamic <- function(w, return_widget = FALSE) {
277290
w$.start_server()
278291
w <- the_anyhtmlwidget(
279292
esm = w$.get_esm(),
@@ -283,11 +296,14 @@ invoke_dynamic <- function(w) {
283296
port = w$.get_port(),
284297
host = w$.get_host()
285298
)
299+
if(return_widget) {
300+
return(w)
301+
}
286302
print(w)
287303
}
288304

289305
#' @keywords internal
290-
invoke_gadget <- function(w) {
306+
invoke_gadget <- function(w, return_widget = FALSE) {
291307
ui <- shiny::tagList(
292308
anyhtmlwidget_output(output_id = "my_widget", width = '100%', height = '100%')
293309
)
@@ -322,6 +338,10 @@ invoke_gadget <- function(w) {
322338
})
323339
}
324340

341+
if(return_widget) {
342+
return(list(ui = ui, server = server))
343+
}
344+
325345
shiny::runGadget(ui, server)
326346
}
327347

tests/testthat.R

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
library(testthat)
2+
library(anyhtmlwidget)
3+
4+
test_check("anyhtmlwidget")

tests/testthat/test-widget.R

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
library(anyhtmlwidget)
2+
3+
esm <- "
4+
function render({ el, model }) {
5+
el.style.border = '4px solid red';
6+
let count = () => model.get('count');
7+
let btn = document.createElement('button');
8+
btn.innerHTML = `count is ${count()}`;
9+
btn.addEventListener('click', () => {
10+
model.set('count', count() + 1);
11+
model.save_changes();
12+
});
13+
model.on('change:count', () => {
14+
btn.innerHTML = `count is ${count()}`;
15+
});
16+
el.appendChild(btn);
17+
}
18+
export default { render };
19+
"
20+
21+
test_that("counter widget can be instantiated", {
22+
w <- AnyHtmlWidget$new(
23+
.esm = esm,
24+
.mode = "static",
25+
.height='400px',
26+
count = 1
27+
)
28+
29+
expect_equal(w$count, 1)
30+
31+
# Check that getters work
32+
expect_equal(w$.get_value("count"), 1)
33+
expect_equal(w$.get_esm(), esm)
34+
expect_equal(w$.get_values(), list(
35+
count = 1
36+
))
37+
expect_equal(w$.get_width(), "100%")
38+
expect_equal(w$.get_height(), '400px')
39+
expect_equal(w$.get_mode(), "static")
40+
expect_equal(w$.get_host(), "0.0.0.0")
41+
expect_true(is.numeric(w$.get_port()))
42+
43+
# Check that setters work
44+
w$.set_value("count", 3, emit_change = FALSE)
45+
expect_equal(w$.get_value("count"), 3)
46+
47+
# Check that onChange handler works.
48+
# Create an empty list to track calls to the handler.
49+
change_list <<- list()
50+
handle_change <- function(key, new_val) {
51+
# Append to the list of { key, val }
52+
# pairs of tracked changes
53+
change_list <<- append(change_list,
54+
list(list(key = key, val = new_val))
55+
)
56+
}
57+
w$.on_change(handle_change)
58+
59+
w$.set_value("count", 5, emit_change = FALSE)
60+
expect_equal(w$.get_value("count"), 5)
61+
expect_equal(length(change_list), 0)
62+
63+
w$.set_value("count", 6, emit_change = TRUE)
64+
expect_equal(w$.get_value("count"), 6)
65+
expect_equal(length(change_list), 1)
66+
expect_equal(change_list[[1]], list(key = "count", val = 6))
67+
68+
w$.set_value("count", 7, emit_change = TRUE)
69+
expect_equal(w$.get_value("count"), 7)
70+
expect_equal(length(change_list), 2)
71+
expect_equal(change_list[[1]], list(key = "count", val = 6))
72+
expect_equal(change_list[[2]], list(key = "count", val = 7))
73+
})
74+
75+
test_that("invalid mode parameter value results in error", {
76+
expect_error(AnyHtmlWidget$new(
77+
.esm = esm,
78+
.mode = "INVALID",
79+
.height='400px',
80+
count = 1
81+
), "Invalid widget mode.")
82+
})
83+
84+
test_that("render return value reflects mode", {
85+
static_w <- AnyHtmlWidget$new(
86+
.esm = esm,
87+
.mode = "static",
88+
.height='400px',
89+
count = 1
90+
)
91+
92+
render_val <- static_w$render(return_widget = TRUE)
93+
expect_equal(class(render_val), c("anyhtmlwidget", "htmlwidget"))
94+
render_val2 <- static_w$.get_htmlwidget()
95+
expect_equal(class(render_val2), c("anyhtmlwidget", "htmlwidget"))
96+
97+
dynamic_w <- AnyHtmlWidget$new(
98+
.esm = esm,
99+
.mode = "dynamic",
100+
.height='400px',
101+
count = 1
102+
)
103+
render_val <- dynamic_w$render(return_widget = TRUE)
104+
expect_equal(class(render_val), c("anyhtmlwidget", "htmlwidget"))
105+
render_val2 <- dynamic_w$.get_htmlwidget()
106+
expect_equal(class(render_val2), c("anyhtmlwidget", "htmlwidget"))
107+
108+
shiny_w <- AnyHtmlWidget$new(
109+
.esm = esm,
110+
.mode = "shiny",
111+
.height='400px',
112+
count = 1
113+
)
114+
expect_error(shiny_w$render(return_widget = TRUE), "render is meant for use with static, gadget, and dynamic modes")
115+
expect_error(shiny_w$.get_htmlwidget(), ".get_htmlwidget is meant for use with static and dynamic modes")
116+
117+
gadget_w <- AnyHtmlWidget$new(
118+
.esm = esm,
119+
.mode = "gadget",
120+
.height='400px',
121+
count = 1
122+
)
123+
render_val <- gadget_w$render(return_widget = TRUE)
124+
expect_equal(class(render_val), c("list"))
125+
expect_equal(names(render_val), c("ui", "server"))
126+
expect_error(gadget_w$.get_htmlwidget(), ".get_htmlwidget is meant for use with static and dynamic modes")
127+
128+
})

0 commit comments

Comments
 (0)