my_board <- new_board( blocks = c( a = new_dataset_block(), b = new_head_block(), c = new_dataset_block(), d = new_scatter_block() ), links = list( from = "c", to = "d", input = "data" ) ) test_draw_link_action <- function(edge, expected_updates) { r_board <- reactiveValues(board = my_board) r_update <- reactiveVal(list()) testServer( function(id, ...) { moduleServer( id, module = draw_link_action( trigger = reactive(edge), board = r_board, update = r_update, dag_extension = list( proxy = g6_proxy( "graph", session = MockShinySession$new() ) ) ) ) }, { expect_length(r_update(), 0L) session$flushReact() upd <- r_update() expect_length(upd, expected_updates) if (expected_updates > 0) { expect_named(upd, "links") expect_length(upd$links, 1L) expect_named(upd$links, "add") expect_length(upd$links$add, 1L) new_link <- as.list(upd$links$add[[1]]) expect_identical(new_link$from, "a") expect_identical(new_link$to, "b") expect_identical(new_link$input, "data") } } ) } test_that("draw link action with valid target", { test_draw_link_action( list(id = "a-b", source = "a", target = "b", targetPort = "node-b-data"), 1L ) }) test_that("remove_selected_action works", { r_board <- reactiveValues(board = my_board) r_update <- reactiveVal(list()) testServer( function(id, ...) { moduleServer( id, module = remove_selected_action( trigger = reactive(TRUE), board = r_board, update = r_update, dag_extension = list( proxy = g6_proxy( "graph", session = MockShinySession$new() ) ) ) ) }, { expect_length(r_update(), 0L) dag_extension$proxy$session$setInputs( "graph-selected_node" = c("a", "b"), "graph-selected_edge" = "c-d" ) upd <- r_update() expect_length(upd, 3L) expect_named(upd, c("blocks", "links", "stacks")) expect_named(upd$blocks, "rm") expect_identical(upd$blocks$rm, input[["graph-selected_node"]]) expect_named(upd$links, "rm") expect_identical(upd$links$rm, input[["graph-selected_edge"]]) expect_named(upd$stacks, "rm") expect_length(upd$stacks$rm, 0L) } ) }) # --- Copy / Cut / Paste fixtures and helpers --- copy_board <- new_board( blocks = c( a = new_dataset_block(), b = new_head_block(), c = new_dataset_block(), d = new_scatter_block() ), links = list(from = "a", to = "b", input = "data"), stacks = list(s1 = new_stack(c("a", "b"))) ) make_clipboard_spy <- function() { spy <- new.env(parent = emptyenv()) spy$msgs <- list() mock_session <- MockShinySession$new() mock_session$sendCustomMessage <- function(type, message) { spy$msgs[[length(spy$msgs) + 1L]] <- list(type = type, message = message) } list(session = mock_session, spy = spy) } # --- copy_selected_action --- test_that("copy_selected_action sends clipboard message for selected nodes", { cs <- make_clipboard_spy() r_board <- reactiveValues(board = copy_board, blocks = list()) r_update <- reactiveVal(list()) testServer( function(id, ...) { moduleServer( id, module = copy_selected_action( trigger = reactive(TRUE), board = r_board, update = r_update, dag_extension = list( proxy = g6_proxy("graph", session = cs$session) ) ) ) }, { expect_length(r_update(), 0L) dag_extension$proxy$session$setInputs( "graph-selected_node" = c("node-a", "node-b") ) session$flushReact() expect_length(cs$spy$msgs, 1L) expect_identical(cs$spy$msgs[[1]]$type, "write-clipboard") json_data <- jsonlite::fromJSON( cs$spy$msgs[[1]]$message$json, simplifyDataFrame = FALSE ) expect_identical(json_data$object, "subboard") upd <- r_update() expect_length(upd, 0L) } ) }) test_that("copy_selected_action does nothing when nothing is selected", { cs <- make_clipboard_spy() r_board <- reactiveValues(board = copy_board, blocks = list()) r_update <- reactiveVal(list()) testServer( function(id, ...) { moduleServer( id, module = copy_selected_action( trigger = reactive(TRUE), board = r_board, update = r_update, dag_extension = list( proxy = g6_proxy("graph", session = cs$session) ) ) ) }, { expect_length(r_update(), 0L) session$flushReact() expect_length(cs$spy$msgs, 0L) expect_length(r_update(), 0L) } ) }) # --- cut_selected_action --- test_that("cut_selected_action copies to clipboard and removes", { cs <- make_clipboard_spy() r_board <- reactiveValues(board = copy_board, blocks = list()) r_update <- reactiveVal(list()) testServer( function(id, ...) { moduleServer( id, module = cut_selected_action( trigger = reactive(TRUE), board = r_board, update = r_update, dag_extension = list( proxy = g6_proxy("graph", session = cs$session) ) ) ) }, { expect_length(r_update(), 0L) dag_extension$proxy$session$setInputs( "graph-selected_node" = c("node-a", "node-b") ) session$flushReact() expect_length(cs$spy$msgs, 1L) expect_identical(cs$spy$msgs[[1]]$type, "write-clipboard") upd <- r_update() expect_length(upd, 3L) expect_named(upd, c("blocks", "links", "stacks")) expect_named(upd$blocks, "rm") expect_true(all(c("a", "b") %in% upd$blocks$rm)) expect_named(upd$links, "rm") expect_named(upd$stacks, "rm") } ) }) test_that("cut_selected_action does nothing when nothing is selected", { cs <- make_clipboard_spy() r_board <- reactiveValues(board = copy_board, blocks = list()) r_update <- reactiveVal(list()) testServer( function(id, ...) { moduleServer( id, module = cut_selected_action( trigger = reactive(TRUE), board = r_board, update = r_update, dag_extension = list( proxy = g6_proxy("graph", session = cs$session) ) ) ) }, { expect_length(r_update(), 0L) session$flushReact() expect_length(cs$spy$msgs, 0L) expect_length(r_update(), 0L) } ) }) # --- paste_action --- test_that("paste_action adds remapped blocks/links/stacks", { sub <- extract_subboard(copy_board, block_ids = c("a", "b")) json_str <- as.character(jsonlite::toJSON( blockr_ser(sub), auto_unbox = TRUE )) r_board <- reactiveValues(board = copy_board) r_update <- reactiveVal(list()) testServer( function(id, ...) { moduleServer( id, module = paste_action( trigger = reactive(json_str), board = r_board, update = r_update, dag_extension = list( proxy = g6_proxy("graph", session = MockShinySession$new()) ) ) ) }, { expect_length(r_update(), 0L) session$flushReact() upd <- r_update() expect_named(upd, c("blocks", "links", "stacks")) expect_named(upd$blocks, "add") expect_named(upd$links, "add") expect_named(upd$stacks, "add") expect_length(upd$blocks$add, 2L) new_block_ids <- names(upd$blocks$add) expect_true(!any(new_block_ids %in% board_block_ids(copy_board))) } ) }) test_that("paste_action ignores invalid JSON", { r_board <- reactiveValues(board = copy_board) r_update <- reactiveVal(list()) testServer( function(id, ...) { moduleServer( id, module = paste_action( trigger = reactive("not valid json {{{"), board = r_board, update = r_update, dag_extension = list( proxy = g6_proxy("graph", session = MockShinySession$new()) ) ) ) }, { expect_length(r_update(), 0L) session$flushReact() expect_length(r_update(), 0L) } ) }) test_that("paste_action with empty subboard produces empty add lists", { empty_sub <- new_subboard(blocks(), links(), stacks()) json_str <- as.character(jsonlite::toJSON( blockr_ser(empty_sub), auto_unbox = TRUE )) r_board <- reactiveValues(board = copy_board) r_update <- reactiveVal(list()) testServer( function(id, ...) { moduleServer( id, module = paste_action( trigger = reactive(json_str), board = r_board, update = r_update, dag_extension = list( proxy = g6_proxy("graph", session = MockShinySession$new()) ) ) ) }, { expect_length(r_update(), 0L) session$flushReact() upd <- r_update() expect_named(upd, c("blocks", "links", "stacks")) expect_length(upd$blocks$add, 0L) expect_length(upd$links$add, 0L) expect_length(upd$stacks$add, 0L) } ) }) # --- remove_subboard() helper --- test_that("remove_subboard() produces correct rm lists", { sb <- extract_subboard(copy_board, block_ids = c("a", "b")) captured <- NULL mock_update <- function(val) captured <<- val remove_subboard(sb, mock_update) expect_named(captured, c("blocks", "links", "stacks")) expect_identical(captured$blocks$rm, names(sb$blocks)) expect_identical(captured$links$rm, names(sb$links)) expect_identical(captured$stacks$rm, names(sb$stacks)) })