tests/testthat/test-structure_tibble.R

test_that("assign_difficulty correctly categorizes suggestions", {
  # easy suggestion
  easy_suggestion <- list(
    title = "Add help text",
    details = "Add contextual help text to the interface",
    components = c("shiny::helpText")
  )
  expect_equal(assign_difficulty(easy_suggestion), "Easy")

  # medium suggestion
  medium_suggestion <- list(
    title = "Implement filters",
    details = "Create filter controls for the data",
    components = c("shiny::selectInput", "shiny::dateRangeInput", "shiny::actionButton")
  )
  expect_equal(assign_difficulty(medium_suggestion), "Medium")

  # hard suggestion
  hard_suggestion <- list(
    title = "Build complex navigation",
    details = "Implement a complex navigation system with multiple conditional steps",
    components = c(
      "shiny::navbarPage",
      "shiny::conditionalPanel",
      "bslib::accordion",
      "shiny::tabsetPanel"
    )
  )
  expect_equal(assign_difficulty(hard_suggestion), "Hard")

  # hard based on complexity keywords
  complex_suggestion <- list(
    title = "Advanced integration",
    details = "Implement system integration with multiple advanced features",
    components = c("shiny::observeEvent", "shiny::reactive")
  )
  expect_equal(assign_difficulty(complex_suggestion), "Hard")
})

test_that("assign_category correctly categorizes suggestions", {
  # navigation category
  nav_suggestion <- list(
    title = "Improve navigation",
    details = "Add tab navigation for better structure"
  )
  expect_equal(assign_category(nav_suggestion, "Progressive Disclosure"), "Navigation")

  # layout category
  layout_suggestion <- list(
    title = "Group related content",
    details = "Use consistent spacing and layout containers"
  )
  expect_equal(assign_category(layout_suggestion, "Visual Hierarchy"), "Layout")

  # content category
  content_suggestion <- list(
    title = "Improve labels",
    details = "Use descriptive text and clear information"
  )
  expect_equal(assign_category(content_suggestion, "Information Scent"), "Content")

  # interaction category
  interaction_suggestion <- list(
    title = "Add filter controls",
    details = "Provide interactive filter inputs for users"
  )
  expect_equal(assign_category(interaction_suggestion, "User Control"), "Interaction")

  # visual design category
  visual_suggestion <- list(
    title = "Establish visual hierarchy",
    details = "Use size, color, and visual emphasis to guide attention"
  )
  expect_equal(assign_category(visual_suggestion, "Visual Hierarchy"), "Visual Design")

  # guidance category
  guidance_suggestion <- list(
    title = "Add onboarding help",
    details = "Provide tutorial and guidance for new users"
  )
  expect_equal(assign_category(guidance_suggestion, "User Onboarding"), "Guidance")

  # fallback to concept-based categorization
  cognitive_load_suggestion <- list(
    title = "Simplify interface",
    details = "Reduce options to minimize burden"
  )
  expect_equal(
    assign_category(cognitive_load_suggestion, "Cognitive Load Theory"),
    "Complexity Management"
  )
})

test_that("flatten_suggestions_to_tibble creates correct structure", {
  # create mock suggestion groups
  suggestion_groups <- list(
    list(
      concept = "Cognitive Load Theory",
      suggestions = list(
        list(
          title = "Limit initial choices",
          details = "Show only core filters by default",
          components = c("bslib::accordion", "shiny::conditionalPanel"),
          rationale = "Reduces cognitive load",
          score = 0.92
        ),
        list(
          title = "Provide smart defaults",
          details = "Pre-select commonly used settings",
          components = c("shiny::selectInput"),
          rationale = "Reduces decision fatigue",
          score = 0.85
        )
      )
    ),
    list(
      concept = "Visual Hierarchy",
      suggestions = list(
        list(
          title = "Establish clear priority",
          details = "Use size and color to guide attention",
          components = c("bslib::card", "bslib::value_box"),
          rationale = "Helps users identify key information",
          score = 0.90
        )
      )
    )
  )

  result <- flatten_suggestions_to_tibble(suggestion_groups)

  # check structure
  expect_s3_class(result, "tbl_df")
  expect_equal(nrow(result), 3)
  expect_equal(ncol(result), 8)

  # check column names
  expected_cols <- c(
    "concept",
    "title",
    "details",
    "components",
    "rationale",
    "score",
    "difficulty",
    "category"
  )
  expect_equal(names(result), expected_cols)

  # check column types
  expect_type(result$concept, "character")
  expect_type(result$title, "character")
  expect_type(result$details, "character")
  expect_type(result$components, "character")
  expect_type(result$rationale, "character")
  expect_type(result$score, "double")
  expect_type(result$difficulty, "character")
  expect_type(result$category, "character")

  # check that components are comma-separated strings
  expect_type(result$components, "character")
  # first row has 2 components, should have comma
  expect_true(grepl(",", result$components[1]))

  # check that rows are sorted by score (descending)
  expect_equal(result$score, sort(result$score, decreasing = TRUE))
  expect_equal(result$score[1], 0.92)
  expect_equal(result$score[3], 0.85)

  # check that difficulty and category are assigned
  expect_true(all(result$difficulty %in% c("Easy", "Medium", "Hard")))
  expect_true(all(nchar(result$category) > 0))
})

test_that("flatten_suggestions_to_tibble handles empty input", {
  result <- flatten_suggestions_to_tibble(list())

  expect_s3_class(result, "tbl_df")
  expect_equal(nrow(result), 0)
  expect_equal(ncol(result), 8)
  expect_equal(
    names(result),
    c(
      "concept",
      "title",
      "details",
      "components",
      "rationale",
      "score",
      "difficulty",
      "category"
    )
  )
})

test_that("bid_structure includes suggestions_tbl column", {
  previous_stage <- tibble::tibble(
    stage = "Interpret",
    problem = "Users are overwhelmed by too many options",
    evidence = "Cognitive load issues reported",
    central_question = "How to reduce overload?",
    timestamp = Sys.time()
  )

  suppressMessages(
    result <- bid_structure(previous_stage)
  )

  # check that suggestions_tbl exists
  expect_true("suggestions_tbl" %in% names(result))

  # check that it's a list column
  expect_true(is.list(result$suggestions_tbl))

  # extract the tibble
  suggestions_tbl <- result$suggestions_tbl[[1]]

  # check structure
  expect_s3_class(suggestions_tbl, "tbl_df")
  expect_gt(nrow(suggestions_tbl), 0)
  expect_equal(ncol(suggestions_tbl), 8)

  # check required columns
  expected_cols <- c(
    "concept",
    "title",
    "details",
    "components",
    "rationale",
    "score",
    "difficulty",
    "category"
  )
  expect_equal(names(suggestions_tbl), expected_cols)

  # verify all rows have valid data
  expect_true(all(nchar(suggestions_tbl$concept) > 0))
  expect_true(all(nchar(suggestions_tbl$title) > 0))
  expect_true(all(nchar(suggestions_tbl$details) > 0))
  expect_true(all(nchar(suggestions_tbl$components) > 0))
  expect_true(all(nchar(suggestions_tbl$rationale) > 0))
  expect_true(all(suggestions_tbl$score >= 0 & suggestions_tbl$score <= 1))
  expect_true(all(suggestions_tbl$difficulty %in% c("Easy", "Medium", "Hard")))
  expect_true(all(nchar(suggestions_tbl$category) > 0))
})

test_that("bid_structure maintains backward compatibility with suggestions column", {
  previous_stage <- tibble::tibble(
    stage = "Interpret",
    problem = "Users need overview and detail modes",
    timestamp = Sys.time()
  )

  suppressMessages(
    result <- bid_structure(previous_stage)
  )

  # check that both formats are available
  expect_true("suggestions" %in% names(result))
  expect_true("suggestions_tbl" %in% names(result))

  # check nested format structure
  suggestions <- result$suggestions
  expect_true(is.list(suggestions))
  expect_gt(length(suggestions), 0)

  # each group should have concept and suggestions
  for (group in suggestions) {
    expect_true("concept" %in% names(group))
    expect_true("suggestions" %in% names(group))
    expect_true(is.character(group$concept))
    expect_true(is.list(group$suggestions))
  }

  # verify tibble has same number of suggestions as nested format
  suggestions_tbl <- result$suggestions_tbl[[1]]
  total_nested <- sum(sapply(suggestions, function(g) length(g$suggestions)))
  expect_equal(nrow(suggestions_tbl), total_nested)
})

test_that("suggestions_tbl can be filtered by difficulty", {
  previous_stage <- tibble::tibble(
    stage = "Interpret",
    problem = "Complex interface with multiple issues",
    evidence = "Users report confusion and cognitive overload",
    central_question = "How to simplify?",
    hook = "First-time users struggle",
    context = "Too many advanced options",
    timestamp = Sys.time()
  )

  suppressMessages(
    result <- bid_structure(previous_stage)
  )

  suggestions_tbl <- result$suggestions_tbl[[1]]

  # filter by difficulty
  easy <- suggestions_tbl[suggestions_tbl$difficulty == "Easy", ]
  medium <- suggestions_tbl[suggestions_tbl$difficulty == "Medium", ]
  hard <- suggestions_tbl[suggestions_tbl$difficulty == "Hard", ]

  # should have suggestions in different difficulty levels
  # (at least one difficulty level should have suggestions)
  total_suggestions <- nrow(easy) + nrow(medium) + nrow(hard)
  expect_equal(total_suggestions, nrow(suggestions_tbl))

  # check that filtering works
  expect_true(all(easy$difficulty == "Easy") || nrow(easy) == 0)
  expect_true(all(medium$difficulty == "Medium") || nrow(medium) == 0)
  expect_true(all(hard$difficulty == "Hard") || nrow(hard) == 0)
})

test_that("suggestions_tbl can be filtered by category", {
  previous_stage <- tibble::tibble(
    stage = "Interpret",
    problem = "Multiple UX issues across interface",
    evidence = "Navigation issues and visual hierarchy problems",
    central_question = "How to improve overall design?",
    timestamp = Sys.time()
  )

  suppressMessages(
    result <- bid_structure(previous_stage)
  )

  suggestions_tbl <- result$suggestions_tbl[[1]]

  # get unique categories
  categories <- unique(suggestions_tbl$category)
  expect_gt(length(categories), 0)

  # filter by first category
  first_category <- categories[1]
  filtered <- suggestions_tbl[suggestions_tbl$category == first_category, ]

  expect_gt(nrow(filtered), 0)
  expect_true(all(filtered$category == first_category))
})

test_that("suggestions_tbl ordering matches score ranking", {
  previous_stage <- tibble::tibble(
    stage = "Interpret",
    problem = "Dashboard needs improvements",
    evidence = "User feedback indicates issues",
    timestamp = Sys.time()
  )

  suppressMessages(
    result <- bid_structure(previous_stage)
  )

  suggestions_tbl <- result$suggestions_tbl[[1]]

  # verify descending order by score
  if (nrow(suggestions_tbl) > 1) {
    for (i in 1:(nrow(suggestions_tbl) - 1)) {
      expect_gte(suggestions_tbl$score[i], suggestions_tbl$score[i + 1])
    }
  }
})

test_that("components are properly comma-separated in tibble", {
  previous_stage <- tibble::tibble(
    stage = "Interpret",
    problem = "Interface needs component recommendations",
    timestamp = Sys.time()
  )

  suppressMessages(
    result <- bid_structure(previous_stage)
  )

  suggestions_tbl <- result$suggestions_tbl[[1]]

  # check that components column exists and is character
  expect_true("components" %in% names(suggestions_tbl))
  expect_type(suggestions_tbl$components, "character")

  # verify comma separation for multi-component suggestions
  multi_component <- suggestions_tbl[grepl(",", suggestions_tbl$components), ]
  if (nrow(multi_component) > 0) {
    # should be able to split by comma
    components_list <- strsplit(multi_component$components[1], ", ")[[1]]
    expect_gt(length(components_list), 1)
    expect_true(all(nchar(trimws(components_list)) > 0))
  }
})

Try the bidux package in your browser

Any scripts or data that you put into this service are public.

bidux documentation built on Nov. 20, 2025, 1:06 a.m.