diff --git a/R/visualizeNetworksWithHTML.R b/R/visualizeNetworksWithHTML.R index a48a012..2c6881e 100644 --- a/R/visualizeNetworksWithHTML.R +++ b/R/visualizeNetworksWithHTML.R @@ -281,8 +281,9 @@ createEdgeElements <- function(edges) { #' used to render a network visualization. It's decoupled from any specific #' UI framework. #' -#' @param node_elements List of node elements created by createNodeElements() -#' @param edge_elements List of edge elements created by createEdgeElements() +#' @param nodes List of nodes from getSubnetworkFromIndra +#' @param edges List of edges from getSubnetworkFromIndra +#' @param display_label_type column of nodes table for displaying node names #' @param container_id ID of the HTML container element (default: 'network-cy') #' @param event_handlers Optional list of event handler configurations #' @param layout_options Optional list of layout configuration options @@ -293,11 +294,16 @@ createEdgeElements <- function(edges) { #' - layout: Layout configuration #' - container_id: Container element ID #' - js_code: Complete JavaScript code (for backward compatibility) -generateCytoscapeConfig <- function(node_elements, edge_elements, +generateCytoscapeConfig <- function(nodes, edges, + display_label_type = "id", container_id = "network-cy", event_handlers = NULL, layout_options = NULL) { + # Create elements + node_elements <- createNodeElements(nodes, display_label_type) + edge_elements <- createEdgeElements(edges) + # Default layout options default_layout <- list( name = "dagre", @@ -517,8 +523,6 @@ convertLayoutToJS <- function(layout_list) { #' @examples #' \dontrun{ #' # Assuming you have nodes and edges data -#' node_elements <- createNodeElements(nodes) -#' edge_elements <- createEdgeElements(edges) #' config <- generateCytoscapeConfig(node_elements, edge_elements) #' #' # Export to HTML @@ -894,12 +898,8 @@ exportNetworkToHTML <- function(nodes, edges, displayLabelType = "id", ...) { - # Create elements - node_elements <- createNodeElements(nodes, displayLabelType) - edge_elements <- createEdgeElements(edges) - # Generate configuration - config <- generateCytoscapeConfig(node_elements, edge_elements) + config <- generateCytoscapeConfig(nodes, edges, display_label_type = displayLabelType) # Export to HTML exportCytoscapeToHTML(config, filename, ...) @@ -917,13 +917,9 @@ exportNetworkToHTML <- function(nodes, edges, previewNetworkInBrowser <- function(nodes, edges, displayLabelType = "id", ...) { - - # Create elements - node_elements <- createNodeElements(nodes, displayLabelType) - edge_elements <- createEdgeElements(edges) - + # Generate configuration - config <- generateCytoscapeConfig(node_elements, edge_elements) + config <- generateCytoscapeConfig(nodes, edges, display_label_type = displayLabelType) # Create temporary filename temp_file <- tempfile(fileext = ".html") diff --git a/man/generateCytoscapeConfig.Rd b/man/generateCytoscapeConfig.Rd index cb62cb3..07c70b8 100644 --- a/man/generateCytoscapeConfig.Rd +++ b/man/generateCytoscapeConfig.Rd @@ -5,17 +5,20 @@ \title{Generate Cytoscape visualization configuration} \usage{ generateCytoscapeConfig( - node_elements, - edge_elements, + nodes, + edges, + display_label_type = "id", container_id = "network-cy", event_handlers = NULL, layout_options = NULL ) } \arguments{ -\item{node_elements}{List of node elements created by createNodeElements()} +\item{nodes}{List of nodes from getSubnetworkFromIndra} -\item{edge_elements}{List of edge elements created by createEdgeElements()} +\item{edges}{List of edges from getSubnetworkFromIndra} + +\item{display_label_type}{column of nodes table for displaying node names} \item{container_id}{ID of the HTML container element (default: 'network-cy')} diff --git a/tests/testthat/test-visualizeNetworksWithHTML.R b/tests/testthat/test-visualizeNetworksWithHTML.R new file mode 100644 index 0000000..7746023 --- /dev/null +++ b/tests/testthat/test-visualizeNetworksWithHTML.R @@ -0,0 +1,327 @@ +# ============================================================================= +# UNIT TESTS FOR NETWORK VISUALIZATION MODULE +# ============================================================================= + +# Load required libraries + +# ============================================================================= +# MOCK DATA SETUP +# ============================================================================= + +# Mock data for testing +create_mock_input_data <- function() { + data.frame( + Protein = c("P53_HUMAN", "MDM2_HUMAN", "ATM_HUMAN", "BRCA1_HUMAN"), + log2FC = c(2.5, -1.8, 1.2, -2.1), + adj.pvalue = c(0.001, 0.02, 0.03, 0.005), + Label = rep("Treatment_vs_Control", 4), + stringsAsFactors = FALSE + ) +} + +create_mock_annotated_data <- function() { + data.frame( + Protein = c("P53_HUMAN", "MDM2_HUMAN", "ATM_HUMAN", "BRCA1_HUMAN"), + log2FC = c(2.5, -1.8, 1.2, -2.1), + adj.pvalue = c(0.001, 0.02, 0.03, 0.005), + Label = rep("Treatment_vs_Control", 4), + HgncId = c("101", "102", "103", "104"), + HgncName = c("TP53", "MDM2", "ATM", "BRCA1"), + stringsAsFactors = FALSE + ) +} + +create_mock_subnetwork_nodes <- function() { + data.frame( + id = c("P53_HUMAN", "MDM2_HUMAN", "ATM_HUMAN", "BRCA1_HUMAN"), + logFC = c(2.5, -1.8, 1.2, -2.1), + pvalue = c(0.001, 0.02, 0.03, 0.005), + hgncName = c("TP53", "MDM2", "ATM", "BRCA1"), + stringsAsFactors = FALSE + ) +} + +create_mock_subnetwork_edges <- function() { + data.frame( + source = c("TP53", "MDM2", "ATM", "TP53"), + target = c("MDM2", "TP53", "TP53", "BRCA1"), + interaction = c("Inhibition", "Activation", "Phosphorylation", "Complex"), + evidenceCount = c(15, 8, 12, 5), + evidenceLink = c("link1", "link2", "link3", "link4"), + source_counts = c("{reach:10, signor:5}", "{reach:5,biopax:3}", "{reach:8,phosphoelm:4}", "{biopax:5}"), + stringsAsFactors = FALSE + ) +} + +create_mock_subnetwork <- function() { + list( + nodes = create_mock_subnetwork_nodes(), + edges = create_mock_subnetwork_edges() + ) +} + +# ============================================================================= +# TESTS FOR COLOR MAPPING FUNCTION +# ============================================================================= + +test_that("mapLogFCToColor handles various input scenarios", { + + # Test normal case with varied logFC values + logFC_values <- c(-2, -1, 0, 1, 2) + colors <- mapLogFCToColor(logFC_values) + expect_equal(length(colors), 5) + expect_true(all(grepl("^#[0-9A-Fa-f]{6}$", colors))) # Valid hex colors + + # Test case with all NA values + na_values <- c(NA, NA, NA) + na_colors <- mapLogFCToColor(na_values) + expect_equal(length(na_colors), 3) + expect_true(all(na_colors == "#D3D3D3")) + + # Test case with all same values + same_values <- c(1, 1, 1) + same_colors <- mapLogFCToColor(same_values) + expect_equal(length(same_colors), 3) + expect_true(all(same_colors == "#D3D3D3")) + + # Test empty input + empty_colors <- mapLogFCToColor(numeric(0)) + expect_equal(length(empty_colors), 0) +}) + +# ============================================================================= +# TESTS FOR RELATIONSHIP PROPERTIES +# ============================================================================= + +test_that("getRelationshipProperties returns correct structure", { + props <- getRelationshipProperties() + + expect_type(props, "list") + expect_true("complex" %in% names(props)) + expect_true("regulatory" %in% names(props)) + expect_true("phosphorylation" %in% names(props)) + expect_true("other" %in% names(props)) + + # Test complex properties + complex_props <- props$complex + expect_true("types" %in% names(complex_props)) + expect_true("Complex" %in% complex_props$types) + expect_equal(complex_props$consolidate, "undirected") + + # Test regulatory properties + reg_props <- props$regulatory + expect_true("colors" %in% names(reg_props)) + expect_true("Inhibition" %in% names(reg_props$colors)) + expect_equal(reg_props$consolidate, "bidirectional") +}) + +# ============================================================================= +# TESTS FOR EDGE CONSOLIDATION +# ============================================================================= + +test_that("consolidateEdges properly consolidates bidirectional relationships", { + + # Create test edges with bidirectional regulatory relationships + test_edges <- data.frame( + source = c("TP53", "MDM2", "ATM", "BRCA1"), + target = c("MDM2", "TP53", "TP53", "ATM"), + interaction = c("Inhibition", "Inhibition", "Phosphorylation", "Complex"), + stringsAsFactors = FALSE + ) + + consolidated <- consolidateEdges(test_edges) + + expect_s3_class(consolidated, "data.frame") + expect_true("edge_type" %in% names(consolidated)) + expect_true("category" %in% names(consolidated)) + + # Should have fewer edges than original due to consolidation + expect_lt(nrow(consolidated), nrow(test_edges)) + + # Check that bidirectional inhibition was consolidated + inhibition_edges <- consolidated[grepl("Inhibition", consolidated$interaction), ] + expect_equal(nrow(inhibition_edges), 1) + expect_equal(inhibition_edges$edge_type, "bidirectional") +}) + +test_that("consolidateEdges handles empty input", { + empty_edges <- data.frame( + source = character(0), + target = character(0), + interaction = character(0), + stringsAsFactors = FALSE + ) + + result <- consolidateEdges(empty_edges) + expect_equal(nrow(result), 0) +}) + +# ============================================================================= +# TESTS FOR EDGE STYLING +# ============================================================================= + +test_that("getEdgeStyle returns appropriate styling", { + + # Test regulatory relationship styling + style <- getEdgeStyle("Inhibition", "regulatory", "directed") + expect_type(style, "list") + expect_true("color" %in% names(style)) + expect_equal(style$color, "#FF4444") # Red for inhibition + + # Test complex relationship styling + complex_style <- getEdgeStyle("Complex", "complex", "undirected") + expect_equal(complex_style$arrow, "none") + expect_equal(complex_style$color, "#8B4513") + + # Test unknown relationship + unknown_style <- getEdgeStyle("Unknown", "other", "directed") + expect_equal(unknown_style$color, "#666666") +}) + +# ============================================================================= +# TESTS FOR NODE ELEMENT CREATION +# ============================================================================= + +test_that("createNodeElements creates proper node structures", { + nodes <- create_mock_subnetwork_nodes() + + # Test with default label type (id) + node_elements <- createNodeElements(nodes, "id") + expect_equal(length(node_elements), nrow(nodes)) + expect_true(all(grepl("data:", node_elements))) + expect_true(all(grepl("id:", node_elements))) + expect_true(all(grepl("label:", node_elements))) + + # Test with hgncName label type + node_elements_hgnc <- createNodeElements(nodes, "hgncName") + expect_equal(length(node_elements_hgnc), nrow(nodes)) + + # Test nodes without logFC column + nodes_no_logfc <- nodes[, !names(nodes) %in% "logFC"] + node_elements_no_logfc <- createNodeElements(nodes_no_logfc, "id") + expect_equal(length(node_elements_no_logfc), nrow(nodes_no_logfc)) +}) + +# ============================================================================= +# TESTS FOR EDGE ELEMENT CREATION +# ============================================================================= + +test_that("createEdgeElements creates proper edge structures", { + edges <- create_mock_subnetwork_edges() + + edge_elements <- createEdgeElements(edges) + expect_type(edge_elements, "list") + expect_gt(length(edge_elements), 0) + + # Check that all elements contain required fields + expect_true(all(sapply(edge_elements, function(x) grepl("source:", x)))) + expect_true(all(sapply(edge_elements, function(x) grepl("target:", x)))) + + # Test empty edges + empty_edges <- data.frame( + source = character(0), + target = character(0), + interaction = character(0), + stringsAsFactors = FALSE + ) + empty_elements <- createEdgeElements(empty_edges) + expect_equal(length(empty_elements), 0) +}) + +# ============================================================================= +# TESTS FOR CYTOSCAPE CONFIG GENERATION +# ============================================================================= + +test_that("generateCytoscapeConfig creates complete configuration", { + nodes <- create_mock_subnetwork_nodes() + edges <- create_mock_subnetwork_edges() + + config <- generateCytoscapeConfig(nodes, edges) + + expect_type(config, "list") + expect_true("elements" %in% names(config)) + expect_true("style" %in% names(config)) + expect_true("layout" %in% names(config)) + expect_true("container_id" %in% names(config)) + expect_true("js_code" %in% names(config)) + + expect_equal(config$container_id, "network-cy") + expect_type(config$js_code, "character") + expect_gt(nchar(config$js_code), 100) +}) + +test_that("generateCytoscapeConfig accepts custom parameters", { + nodes <- create_mock_subnetwork_nodes() + edges <- create_mock_subnetwork_edges() + + custom_layout <- list(name = "grid", fit = FALSE) + custom_handlers <- list(edge_click = "function() { console.log('test'); }") + + config <- generateCytoscapeConfig( + nodes, + edges, + container_id = "custom-container", + event_handlers = custom_handlers, + layout_options = custom_layout + ) + + expect_equal(config$container_id, "custom-container") + expect_equal(config$layout$name, "grid") + expect_false(config$layout$fit) + expect_true(grepl("console.log", config$js_code)) +}) + + +# ============================================================================= +# TESTS FOR STYLE CONVERSION FUNCTIONS +# ============================================================================= + +test_that("convertStyleToJS creates valid JavaScript", { + style_list <- list( + list( + selector = "node", + style = list( + `background-color` = "data(color)", + width = "60px" + ) + ) + ) + + js_style <- convertStyleToJS(style_list) + expect_type(js_style, "character") + expect_true(grepl("selector", js_style)) + expect_true(grepl("background-color", js_style)) + expect_true(grepl("data\\(color\\)", js_style)) +}) + +test_that("convertLayoutToJS creates valid JavaScript", { + layout_list <- list( + name = "dagre", + fit = TRUE, + padding = 30 + ) + + js_layout <- convertLayoutToJS(layout_list) + expect_type(js_layout, "character") + expect_true(grepl("\"name\": \"dagre\"", js_layout)) + expect_true(grepl("\"fit\": true", js_layout)) + expect_true(grepl("\"padding\": 30", js_layout)) +}) + +test_that("createNodeElements handles different label types", { + nodes <- create_mock_subnetwork_nodes() + + # Test with id labels + elements_id <- createNodeElements(nodes, "id") + expect_true(all(grepl("P53_HUMAN|MDM2_HUMAN|ATM_HUMAN|BRCA1_HUMAN", elements_id))) + + # Test with hgncName labels + elements_hgnc <- createNodeElements(nodes, "hgncName") + expect_true(all(grepl("TP53|MDM2|ATM|BRCA1", elements_hgnc))) + + # Test with nodes missing hgncName + nodes_no_hgnc <- nodes + nodes_no_hgnc$hgncName <- NA + elements_fallback <- createNodeElements(nodes_no_hgnc, "hgncName") + expect_true(all(grepl("P53_HUMAN|MDM2_HUMAN|ATM_HUMAN|BRCA1_HUMAN", elements_fallback))) +}) \ No newline at end of file