test_that("Shortcode detection in text content", { # Test basic shortcode detection expect_true(rmd_has_shortcode("Hello {{< myfunc >}} world")) expect_false(rmd_has_shortcode("Hello world")) # Test multiple shortcodes expect_true(rmd_has_shortcode("{{< func1 >}} and {{< func2 >}}")) # Test shortcode with arguments expect_true(rmd_has_shortcode("{{< myfunc arg1 arg2 >}}")) expect_true(rmd_has_shortcode("{{< myfunc \"arg with spaces\" >}}")) # Test function name filtering expect_true(rmd_has_shortcode("{{< video url >}}", "video")) expect_false(rmd_has_shortcode("{{< pagebreak >}}", "video")) # Test glob patterns expect_true(rmd_has_shortcode("{{< video-embed >}}", "video*")) expect_false(rmd_has_shortcode("{{< pagebreak >}}", "video*")) }) test_that("Shortcode detection in AST nodes", { # Test shortcode detection in markdown rmd1 = parse_rmd(c("Hello {{< video url >}} world")) expect_equal(rmd_select(rmd1, has_shortcode()), rmd1[1]) expect_equal(rmd_select(rmd1, has_shortcode("video")), rmd1[1]) expect_length(rmd_select(rmd1, has_shortcode("pagebreak")), 0) # Test shortcode detection in chunk code rmd2 = parse_rmd(c("```{r}", "print('{{< pagebreak >}}')", "```")) expect_equal(rmd_select(rmd2, has_shortcode()), rmd2[1]) expect_equal(rmd_select(rmd2, has_shortcode("pagebreak")), rmd2[1]) expect_length(rmd_select(rmd2, has_shortcode("video")), 0) # Test no shortcodes rmd3 = parse_rmd(c("Hello world", "```{r}", "print('hello')", "```")) expect_length(rmd_select(rmd3, has_shortcode()), 0) }) test_that("rmd_has_shortcode() with different rmd classes", { # Test rmd_ast objects rmd_ast_with_shortcode = parse_rmd(c("# Title", "Text with {{< video url >}} shortcode")) rmd_ast_without_shortcode = parse_rmd(c("# Title", "Just normal text")) expect_true(any(rmd_has_shortcode(rmd_ast_with_shortcode))) expect_false(any(rmd_has_shortcode(rmd_ast_without_shortcode))) expect_true(any(rmd_has_shortcode(rmd_ast_with_shortcode, "video"))) expect_false(any(rmd_has_shortcode(rmd_ast_with_shortcode, "pagebreak"))) # Test rmd_markdown objects rmd_markdown_with_shortcode = parse_rmd("Text with {{< kbd Ctrl+C >}} shortcode")[[1]] rmd_markdown_without_shortcode = parse_rmd("Just normal text")[[1]] expect_true(rmd_has_shortcode(rmd_markdown_with_shortcode)) expect_false(rmd_has_shortcode(rmd_markdown_without_shortcode)) expect_true(rmd_has_shortcode(rmd_markdown_with_shortcode, "kbd")) expect_false(rmd_has_shortcode(rmd_markdown_with_shortcode, "video")) # Test rmd_chunk objects rmd_chunk_with_shortcode = parse_rmd(c("```{r}", "# Comment with {{< include file.txt >}}", "x = 1", "```"))[[1]] rmd_chunk_without_shortcode = parse_rmd(c("```{r}", "x = 1", "```"))[[1]] expect_true(rmd_has_shortcode(rmd_chunk_with_shortcode)) expect_false(rmd_has_shortcode(rmd_chunk_without_shortcode)) expect_true(rmd_has_shortcode(rmd_chunk_with_shortcode, "include")) expect_false(rmd_has_shortcode(rmd_chunk_with_shortcode, "video")) # Test rmd_yaml objects (shortcodes must be in quoted values) rmd_yaml_with_shortcode = parse_rmd(c("---", "title: \"Document with {{< var title >}}\"", "---"))[[1]] rmd_yaml_without_shortcode = parse_rmd(c("---", "title: My Document", "---"))[[1]] # Test unquoted shortcode (should be treated as regular YAML value, but might still be detected) rmd_yaml_unquoted_shortcode = parse_rmd(c("---", "title: {{< var title >}}", "---"))[[1]] expect_true(rmd_has_shortcode(rmd_yaml_with_shortcode)) expect_false(rmd_has_shortcode(rmd_yaml_without_shortcode)) expect_true(rmd_has_shortcode(rmd_yaml_with_shortcode, "var")) expect_false(rmd_has_shortcode(rmd_yaml_with_shortcode, "video")) # Check if unquoted shortcode is handled (depends on YAML parser behavior) # This tests how the YAML parser treats unquoted shortcode-like syntax # Test rmd_heading objects (should return FALSE via default method) rmd_heading_obj = parse_rmd("# My Title")[[1]] expect_false(rmd_has_shortcode(rmd_heading_obj)) expect_false(rmd_has_shortcode(rmd_heading_obj, "video")) # Test rmd_tibble objects rmd_tibble_with_shortcode = as_tibble(parse_rmd("Text with {{< video url >}} shortcode")) rmd_tibble_without_shortcode = as_tibble(parse_rmd("Just normal text")) expect_true(any(rmd_has_shortcode(rmd_tibble_with_shortcode))) expect_false(any(rmd_has_shortcode(rmd_tibble_without_shortcode))) expect_true(any(rmd_has_shortcode(rmd_tibble_with_shortcode, "video"))) expect_false(any(rmd_has_shortcode(rmd_tibble_with_shortcode, "pagebreak"))) }) test_that("has_shortcode() selection helper with different rmd classes", { # Create a comprehensive test document with various node types and shortcodes test_rmd = parse_rmd(c( "---", "title: \"Document with {{< var title >}}\"", "---", "", "# Section 1", "", "Text with {{< video demo.mp4 >}} shortcode.", "", "```{r chunk1}", "# Code with {{< include script.R >}} shortcode", "x = 1", "```", "", "## Subsection", "", "Normal text without shortcodes.", "", "```{r chunk2}", "y = 2", "```", "", "Final text with {{< kbd Ctrl+C >}} and {{< pagebreak >}}." )) # Test selecting all nodes with shortcodes shortcode_nodes = rmd_select(test_rmd, has_shortcode()) expect_equal(shortcode_nodes, test_rmd[c(1,3,4,8)]) # YAML, markdown, chunk, and final markdown # Test selecting nodes with specific shortcode function names video_nodes = rmd_select(test_rmd, has_shortcode("video"), keep_yaml = FALSE) expect_equal(video_nodes, test_rmd[3]) kbd_nodes = rmd_select(test_rmd, has_shortcode("kbd"), keep_yaml = FALSE) expect_equal(kbd_nodes, test_rmd[8]) include_nodes = rmd_select(test_rmd, has_shortcode("include"), keep_yaml = FALSE) expect_equal(include_nodes, test_rmd[4]) var_nodes = rmd_select(test_rmd, has_shortcode("var")) expect_equal(var_nodes, test_rmd[1]) # Test glob patterns page_nodes = rmd_select(test_rmd, has_shortcode("page*"), keep_yaml = FALSE) expect_equal(page_nodes, test_rmd[8]) # Test non-existent shortcode nonexistent_nodes = rmd_select(test_rmd, has_shortcode("nonexistent"), keep_yaml = FALSE) expect_equal(nonexistent_nodes, rmd_ast(list())) # Test multiple function names multi_nodes = rmd_select(test_rmd, has_shortcode(c("video", "kbd")), keep_yaml = FALSE) expect_equal(multi_nodes, test_rmd[c(3,8)]) # Test combining with other selectors chunk_with_shortcode = rmd_select(test_rmd, has_type("rmd_chunk") & has_shortcode(), keep_yaml = FALSE) expect_equal(chunk_with_shortcode, test_rmd[4]) markdown_with_shortcode = rmd_select(test_rmd, has_type("rmd_markdown") & has_shortcode(), keep_yaml = FALSE) expect_equal(markdown_with_shortcode, test_rmd[c(3,8)]) yaml_with_shortcode = rmd_select(test_rmd, has_type("rmd_yaml") & has_shortcode()) expect_equal(yaml_with_shortcode, test_rmd[1]) }) test_that("has_shortcode() with edge cases", { # Test empty document # Test document with no shortcodes no_shortcode_rmd = parse_rmd(c("# Title", "Just normal text", "```{r}", "x = 1", "```")) expect_length(rmd_select(no_shortcode_rmd, has_shortcode()), 0) expect_length(rmd_select(no_shortcode_rmd, has_shortcode("video")), 0) # Test document with only headings (which use default method) headings_only_rmd = parse_rmd(c("# Title 1", "## Subtitle", "### Subsubtitle")) expect_length(rmd_select(headings_only_rmd, has_shortcode()), 0) # Test with shortcode in various positions (multiple lines get combined into one markdown node) complex_rmd = parse_rmd(c( "Start {{< video start >}} text", "Middle text {{< pagebreak >}} more text", "End text {{< include end >}}" )) expect_length(rmd_select(complex_rmd, has_shortcode()), 1) # One markdown node with multiple shortcodes expect_length(rmd_select(complex_rmd, has_shortcode("video")), 1) expect_length(rmd_select(complex_rmd, has_shortcode("pagebreak")), 1) expect_length(rmd_select(complex_rmd, has_shortcode("include")), 1) }) test_that("YAML with multiple shortcodes in list values", { # Test complex YAML document with multiple shortcodes in categories list complex_yaml_doc = c( '---', 'title: "ABC"', 'categories:', ' - "{{< var kws.ds >}}"', ' - "{{< var kws.ml >}}"', ' - "{{< var kws.nlp >}}"', ' - "{{< var kws.python_pkg >}}"', ' - AI', '---', '', 'Here is the content of the post.' ) # Parse the document from text rmd_parsed = parse_rmd(complex_yaml_doc) expect_length(rmd_parsed, 2) # YAML node + markdown node yaml_node_parsed = rmd_parsed[[1]] markdown_node = rmd_parsed[[2]] # Create equivalent YAML node using rmd_yaml() constructor yaml_node_constructed = rmd_yaml(list( title = "ABC", categories = c( "{{< var kws.ds >}}", "{{< var kws.ml >}}", "{{< var kws.nlp >}}", "{{< var kws.python_pkg >}}", "AI" ) )) # Compare parsed vs constructed YAML nodes expect_equal(yaml_node_parsed, yaml_node_constructed) # Test shortcode detection on parsed YAML expect_true(rmd_has_shortcode(yaml_node_parsed)) expect_true(rmd_has_shortcode(yaml_node_parsed, "var")) expect_false(rmd_has_shortcode(yaml_node_parsed, "video")) expect_false(rmd_has_shortcode(yaml_node_parsed, "kws*")) # kws.ds is argument, not function name # Test markdown node (should not have shortcodes) expect_false(rmd_has_shortcode(markdown_node)) # Test selection helpers on parsed document shortcode_nodes = rmd_select(rmd_parsed, has_shortcode()) expect_length(shortcode_nodes, 1) # Only YAML node var_nodes = rmd_select(rmd_parsed, has_shortcode("var")) expect_length(var_nodes, 1) expect_equal(rmd_node_type(var_nodes[[1]]), "rmd_yaml") # Test shortcode extraction from all category values # Position information now reflects position within each individual YAML value expect_equal( rmd_extract_shortcodes(yaml_node_parsed, flatten = TRUE), list( rmd_shortcode("var", "kws.ds", 0L, 18L), rmd_shortcode("var", "kws.ml", 0L, 18L), rmd_shortcode("var", "kws.nlp", 0L, 19L), rmd_shortcode("var", "kws.python_pkg", 0L, 26L) ) ) # Test shortcode extraction with flatten=FALSE (default) - returns nested structure expect_equal( rmd_extract_shortcodes(yaml_node_parsed, flatten = FALSE), list(yaml = list( "title" = list(list()), "categories" = list( list(rmd_shortcode("var", "kws.ds", 0L, 18L)), list(rmd_shortcode("var", "kws.ml", 0L, 18L)), list(rmd_shortcode("var", "kws.nlp", 0L, 19L)), list(rmd_shortcode("var", "kws.python_pkg", 0L, 26L)), list() ) ) ) ) }) test_that("Shortcode extraction functionality", { # Test basic extraction with flatten=TRUE expect_equal( rmd_extract_shortcodes("Hello {{< video url >}} world", flatten = TRUE), list(rmd_shortcode("video", "url", 6L, 17L)) ) # Test multiple shortcodes with flatten=TRUE expect_equal( rmd_extract_shortcodes("{{< func1 >}} and {{< func2 arg >}}", flatten = TRUE), list( rmd_shortcode("func1", character(), 0L, 13L), rmd_shortcode("func2", "arg", 18L, 17L) ) ) # Test no shortcodes with flatten=TRUE expect_equal(rmd_extract_shortcodes("Hello world", flatten = TRUE), list()) # Test flatten=FALSE (default behavior) with vector expect_equal( rmd_extract_shortcodes(c("Text with {{< video url >}}", "Another {{< pagebreak >}} text"), flatten = FALSE), list( list(rmd_shortcode("video", "url", 10L, 17L)), list(rmd_shortcode("pagebreak", character(), 8L, 17L)) ) ) # Test flatten=TRUE with vector expect_equal( rmd_extract_shortcodes(c("Text with {{< video url >}}", "Another {{< pagebreak >}} text"), flatten = TRUE), list( rmd_shortcode("video", "url", 10L, 17L), rmd_shortcode("pagebreak", character(), 8L, 17L) ) ) # Test flatten=TRUE with no shortcodes expect_equal(rmd_extract_shortcodes("No shortcodes here", flatten = TRUE), list()) # Test flatten=FALSE (default) with single strings - returns nested structure expect_equal( rmd_extract_shortcodes("Hello {{< video url >}} world", flatten = FALSE), list(list(rmd_shortcode("video", "url", 6L, 17L))) ) expect_equal( rmd_extract_shortcodes("{{< func1 >}} and {{< func2 arg >}}", flatten = FALSE), list(list( rmd_shortcode("func1", character(), 0L, 13L), rmd_shortcode("func2", "arg", 18L, 17L) )) ) expect_equal(rmd_extract_shortcodes("Hello world", flatten = FALSE), list(list())) # Test complex shortcode patterns expect_true(rmd_has_shortcode("{{< video https://example.com >}}")) expect_true(rmd_has_shortcode("{{< pagebreak >}}")) }) test_that("rmd_extract_shortcodes works across all node types", { # Test rmd_ast extraction test_ast = parse_rmd(c( "---", "title: \"{{< var title >}}\"", "---", "", "# Heading with {{< shortcode >}}", "", "Markdown with {{< video url >}}", "", "```{r}", "# Comment with {{< include file >}}", "```" )) ast_result = rmd_extract_shortcodes(test_ast, flatten = TRUE) expect_length(ast_result, 4) expect_equal(ast_result[[1]]@func, "var") expect_equal(ast_result[[2]]@func, "shortcode") expect_equal(ast_result[[3]]@func, "video") expect_equal(ast_result[[4]]@func, "include") # Test individual node types yaml_node = rmd_yaml(yaml = list(title = "Test {{< var value >}}")) yaml_result = rmd_extract_shortcodes(yaml_node, flatten = TRUE) expect_length(yaml_result, 1) expect_equal(yaml_result[[1]]@func, "var") markdown_node = rmd_markdown(lines = c("Text with {{< video test.mp4 >}} shortcode")) markdown_result = rmd_extract_shortcodes(markdown_node, flatten = TRUE) expect_length(markdown_result, 1) expect_equal(markdown_result[[1]]@func, "video") chunk_node = rmd_chunk(engine = "r", code = c("# {{< include script.R >}}")) chunk_result = rmd_extract_shortcodes(chunk_node, flatten = TRUE) expect_length(chunk_result, 1) expect_equal(chunk_result[[1]]@func, "include") raw_chunk_node = rmd_raw_chunk(format = "html", code = c("
{{< var content >}}
")) raw_result = rmd_extract_shortcodes(raw_chunk_node, flatten = TRUE) expect_length(raw_result, 1) expect_equal(raw_result[[1]]@func, "var") code_block_node = rmd_code_block(code = c("function() { /* {{< comment >}} */ }")) code_result = rmd_extract_shortcodes(code_block_node, flatten = TRUE) expect_length(code_result, 1) expect_equal(code_result[[1]]@func, "comment") code_literal_node = rmd_code_block_literal(code = c("{{< literal >}}")) literal_result = rmd_extract_shortcodes(code_literal_node, flatten = TRUE) expect_length(literal_result, 1) expect_equal(literal_result[[1]]@func, "literal") heading_node = rmd_heading(name = "Title with {{< var heading >}}", level = 1L) heading_result = rmd_extract_shortcodes(heading_node, flatten = TRUE) expect_length(heading_result, 1) expect_equal(heading_result[[1]]@func, "var") span_node = rmd_span(text = "Span with {{< shortcode >}} content") span_result = rmd_extract_shortcodes(span_node, flatten = TRUE) expect_length(span_result, 1) expect_equal(span_result[[1]]@func, "shortcode") shortcode_node = rmd_shortcode(func = "test", args = c("arg1", "{{< nested >}}")) shortcode_result = rmd_extract_shortcodes(shortcode_node, flatten = TRUE) expect_length(shortcode_result, 1) expect_equal(shortcode_result[[1]]@func, "nested") # Test nodes that should return empty inline_code_node = rmd_inline_code(engine = "r", code = "test") expect_length(rmd_extract_shortcodes(inline_code_node, flatten = TRUE), 0) fdiv_open_node = rmd_fenced_div_open() expect_length(rmd_extract_shortcodes(fdiv_open_node, flatten = TRUE), 0) fdiv_close_node = rmd_fenced_div_close() expect_length(rmd_extract_shortcodes(fdiv_close_node, flatten = TRUE), 0) }) test_that("rmd_extract_shortcodes flatten parameter works correctly", { # Test nested structure (flatten = FALSE) markdown_node = rmd_markdown(lines = c("Line 1 {{< first >}}", "Line 2 {{< second >}}")) nested_result = rmd_extract_shortcodes(markdown_node, flatten = FALSE) expect_equal(names(nested_result), "lines") expect_length(nested_result$lines, 2) expect_length(nested_result$lines[[1]], 1) expect_length(nested_result$lines[[2]], 1) # Test flattened structure (flatten = TRUE) flat_result = rmd_extract_shortcodes(markdown_node, flatten = TRUE) expect_length(flat_result, 2) expect_equal(flat_result[[1]]@func, "first") expect_equal(flat_result[[2]]@func, "second") })