Skip to content
This repository is currently being migrated. It's locked while the migration is in progress.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/set-tugboat-tests-pending.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ jobs:
va/tests/cypress
va/tests/phpunit
va/tests/content-build-gql
va/tests/next-build
va/tests/status-error
)
for test_name in "${test_names[@]}"; do
Expand Down
7 changes: 7 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,7 @@
"va:test:check-node-revision-logs": "Check the node revision log fields.",
"va:test:check-taxonomy-revision-logs": "Check the taxonomy revision log fields.",
"va:test:content-build-gql": "Run GraphQL export to test content-build.",
"va:test:next-build": "Test Next Build's use of JSON:API and Decoupled Router.",
"va:test:cypress": "Run Cypress tests.",
"va:test:cypress-ci": "Run Cypress tests with CI-specific functionality.",
"va:test:cypress-interactive": "Run Cypress tests interactively (on host web browser).",
Expand Down Expand Up @@ -555,6 +556,12 @@
"! ./scripts/should-run-directly.sh || ./tests/scripts/content-build-gql.sh",
"./scripts/should-run-directly.sh || ddev composer va:test:content-build-gql --"
],
"va:test:next-build": [
"# Test Next Build's use of JSON:API and Decoupled Router.",
"# Auto-detects env from CMS_ENVIRONMENT_TYPE, or pass env name to override (e.g., tugboat)",
"! ./scripts/should-run-directly.sh || ./tests/scripts/next-build.sh \"$@\"",
"./scripts/should-run-directly.sh || ddev composer va:test:next-build -- \"$@\""
Comment on lines +562 to +563
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These script lines add an extra empty argument when composer forwards arguments to the command (because "$@" expands to empty in this context), which shifts positional parameters for next-build.sh (e.g., composer va:test:next-build tugboat would likely arrive as $1="", $2="tugboat"). This makes the env override unreliable. Prefer omitting "$@" here and rely on Composer’s normal argument forwarding to append args to the command.

Suggested change
"! ./scripts/should-run-directly.sh || ./tests/scripts/next-build.sh \"$@\"",
"./scripts/should-run-directly.sh || ddev composer va:test:next-build -- \"$@\""
"! ./scripts/should-run-directly.sh || ./tests/scripts/next-build.sh",
"./scripts/should-run-directly.sh || ddev composer va:test:next-build --"

Copilot uses AI. Check for mistakes.
],
"va:test:cypress": [
"# Run Cypress tests.",
"! ./scripts/should-run-directly.sh || ./tests/scripts/cypress-tests.sh",
Expand Down
13 changes: 13 additions & 0 deletions tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ tasks:
CONTEXT: va/tests/content-build-gql
DESCRIPTION: 'Ensure that the content build graphql queries succeed'

- task: set-check-status
vars:
STATUS: pending
CONTEXT: va/tests/next-build
DESCRIPTION: 'Ensure that Next Build JSON:API and Decoupled Router queries succeed'

va/tests/cypress:
desc: Accessibility and Behavioral tests with Cypress
# See also `composer va:test:cypress-parallel` in composer.json
Expand Down Expand Up @@ -75,6 +81,12 @@ tasks:
cmds:
- ./tests/scripts/ci-wrapper.sh content-build-gql 'va:test:content-build-gql'

va/tests/next-build:
desc: Ensure that Next Build JSON:API and Decoupled Router queries succeed
# See also `composer va:test:next-build` in composer.json
cmds:
- ./tests/scripts/ci-wrapper.sh next-build 'va:test:next-build'

set-check-status:
cmds:
- |
Expand All @@ -88,4 +100,5 @@ tasks:
- va/tests/cypress
- va/tests/phpunit
- va/tests/content-build-gql
- va/tests/next-build
- va/tests/status-error
225 changes: 225 additions & 0 deletions tests/scripts/next-build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
#!/usr/bin/env bash
# Tests Next Build's use of JSON:API and the Decoupled Router.
#
# Usage: next-build.sh [env-name]
# env-name: Optional environment name (e.g., tugboat, dev, staging, prod)
# Maps to next/envs/.env.{env-name}
# If not provided, auto-detects from CMS_ENVIRONMENT_TYPE

set -eo pipefail

# Number of pages to test per content type during export test
EXPORT_TEST_PAGES_PER_CONTENT_TYPE=3

# Pages that should always be tested (e.g., known edge cases, previously failing pages)
ALWAYS_TEST_PAGES=(
/salt-lake-city-health-care/news-releases/new-data-shows-veterans-increased-use-of-online-va
)

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
source ~/.bashrc

repo_root="$(git rev-parse --show-toplevel)"
# shellcheck source=/dev/null
source "${repo_root}/.env"

# Determine APP_ENV: use argument if provided, otherwise auto-detect from CMS_ENVIRONMENT_TYPE
if [ -n "${1:-}" ]; then
if [ "$1" = "local" ]; then
APP_ENV="example"
else
APP_ENV="$1"
fi
else
# Auto-detect based on CMS_ENVIRONMENT_TYPE
case "${CMS_ENVIRONMENT_TYPE:-}" in
tugboat) APP_ENV="tugboat" ;;
prod) APP_ENV="prod" ;;
staging) APP_ENV="staging" ;;
dev) APP_ENV="dev" ;;
*) APP_ENV="" ;; # Fall back to env-loader defaults
esac
fi

if [ -n "${APP_ENV}" ]; then
echo "Using APP_ENV: ${APP_ENV}"
fi

DRUPAL_ADDRESS="${DRUPAL_ADDRESS:-${DRUSH_OPTIONS_URI:-http://localhost}}"
FAILURES=0

# Generic HTTP test helper - returns body on stdout, exits non-zero on failure
http_get() {
local url="$1"
local response body http_code
response=$(curl -gsS -w '\n%{http_code}' "$url" 2>&1) || return 1
http_code="${response##*$'\n'}"
body="${response%$'\n'*}"

if [[ "$http_code" != 200 ]]; then
echo "HTTP $http_code: $url" >&2
return 1
fi
printf '%s' "$body"
}

run_test() {
local name="$1"; shift
echo
echo "Test: $name starting..."

$@
exit_code=$?

echo

if [ "$exit_code" = "0" ]; then
echo "[PASSED $name]"
return 0
else
echo "[FAILED $name]"
((FAILURES++))
Comment on lines +73 to +83
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

set -e is enabled globally, but this harness is written to continue running and aggregate failures. As written, any failing test command at line 73 will terminate the script before $? is captured, and even if that’s fixed the first ((FAILURES++)) will exit the script because it returns status 1 when incrementing from 0 under set -e. Either drop global -e for this script, or wrap the test invocation and the failure counter increment in constructs that won’t trigger errexit (e.g., if ...; then / else, or temporarily set +e). Also invoke the command as "$@" to preserve argument boundaries.

Suggested change
$@
exit_code=$?
echo
if [ "$exit_code" = "0" ]; then
echo "[PASSED $name]"
return 0
else
echo "[FAILED $name]"
((FAILURES++))
if "$@"; then
echo
echo "[PASSED $name]"
return 0
else
echo
echo "[FAILED $name]"
FAILURES=$((FAILURES + 1))

Copilot uses AI. Check for mistakes.
return 1
fi
}

# Test assertions
assert_json_path() {
local body="$1" path="$2"
jq -e "$path" <<<"$body" >/dev/null
}

# Get sample paths from a content type via JSON:API
# Returns path aliases (one per line) on stdout
get_sample_paths() {
local content_type="$1"
local limit="${EXPORT_TEST_PAGES_PER_CONTENT_TYPE:-1}"
local body
body=$(http_get "${DRUPAL_ADDRESS}/jsonapi/node/${content_type}?page[limit]=${limit}&filter[status]=1" 2>/dev/null) || return 0
jq -r '.data[].attributes.path.alias // empty' <<<"$body" 2>/dev/null
Comment on lines +100 to +101
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get_sample_paths swallows any HTTP/JSON:API errors (|| return 0 and redirects stderr), which can silently drop an enabled content type from SSG_CHERRY_PICKED_PATHS and let the export test pass without covering that type. Consider returning non-zero (or at least logging a warning and tracking a failure) when the JSON:API request fails.

Suggested change
body=$(http_get "${DRUPAL_ADDRESS}/jsonapi/node/${content_type}?page[limit]=${limit}&filter[status]=1" 2>/dev/null) || return 0
jq -r '.data[].attributes.path.alias // empty' <<<"$body" 2>/dev/null
body=$(http_get "${DRUPAL_ADDRESS}/jsonapi/node/${content_type}?page[limit]=${limit}&filter[status]=1") || {
echo "Warning: Failed to fetch sample paths for content type '${content_type}' from JSON:API at ${DRUPAL_ADDRESS}" >&2
((FAILURES++))
return 1
}
jq -r '.data[].attributes.path.alias // empty' <<<"$body"

Copilot uses AI. Check for mistakes.
}

# Get enabled content types from the env file
get_enabled_content_types() {
local env_file="${repo_root}/next/envs/.env.${APP_ENV:-example}"
if [[ ! -f "$env_file" ]]; then
echo "Warning: env file not found: $env_file" >&2
return
fi
Comment on lines +106 to +110
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the Next Build env file is missing, the script only emits a warning and continues, which can make the export test run with just ALWAYS_TEST_PAGES and not validate configured content types. Treat a missing env file as a hard failure for this test so it can’t pass in a misconfigured environment.

Copilot uses AI. Check for mistakes.
# Parse FEATURE_NEXT_BUILD_CONTENT_*=true, extract content type, convert to lowercase
grep -E '^FEATURE_NEXT_BUILD_CONTENT_[A-Z_]+=true' "$env_file" \
| sed 's/^FEATURE_NEXT_BUILD_CONTENT_//; s/=true$//' \
| tr '[:upper:]' '[:lower:]'
}

# Build sample paths for all enabled content types
build_sample_paths() {
local paths=()
local content_types
mapfile -t content_types < <(get_enabled_content_types)

# Include always-test pages first
if [[ ${#ALWAYS_TEST_PAGES[@]} -gt 0 ]]; then
echo "Adding ${#ALWAYS_TEST_PAGES[@]} always-test page(s)..." >&2
paths+=("${ALWAYS_TEST_PAGES[@]}")
fi

echo "Gathering ${EXPORT_TEST_PAGES_PER_CONTENT_TYPE} sample path(s) for ${#content_types[@]} content types from .env.${APP_ENV:-example}..." >&2
for ct in "${content_types[@]}"; do
local ct_paths
mapfile -t ct_paths < <(get_sample_paths "$ct")
if [[ ${#ct_paths[@]} -gt 0 ]]; then
paths+=("${ct_paths[@]}")
echo " ${ct}: ${#ct_paths[@]} path(s)" >&2
else
echo " ${ct}: (no content found)" >&2
fi
done

echo "Total paths to build: ${#paths[@]}" >&2

# Join with semicolons
local IFS=';'
echo "${paths[*]}"
}

router_get_uuid() {
local path="$1"
local body
body=$(http_get "${DRUPAL_ADDRESS}/router/translate-path?path=$(jq -rn --arg p "$path" '$p|@uri')") || return 1
assert_json_path "$body" '.jsonapi.individual' || return 1
jq -r '.entity.uuid' <<<"$body"
}

router_has_uuid() {
local uuid
uuid=$(router_get_uuid "$1") || return 1
[[ -n "$uuid" && "$uuid" != "null" ]]

printf "$uuid... "
}
Comment on lines +156 to +162
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function relies on a standalone [[ ... ]] test for success. With set -e enabled, a false condition will terminate the entire script instead of returning non-zero to run_test. Make this check explicitly control flow (e.g., [[ ... ]] || return 1) so failures are reported correctly.

Copilot uses AI. Check for mistakes.

jsonapi_has_data() {
local endpoint="$1"
local body
body=$(http_get "${DRUPAL_ADDRESS}${endpoint}") || return 1

echo $body | jq -rj '.data[0].id'
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

echo $body | jq ... will word-split/glob-expand the JSON and can also be misinterpreted as echo options (e.g., if JSON starts with -n), causing jq parsing failures or incorrect output. Feed jq the variable without echo and with proper quoting (e.g., via a here-string or printf '%s').

Suggested change
echo $body | jq -rj '.data[0].id'
jq -rj '.data[0].id' <<<"$body"

Copilot uses AI. Check for mistakes.
printf '... '

assert_json_path "$body" '.data[0].id'
}

router_to_jsonapi_flow() {
local path="$1" content_type="$2"
local uuid jsonapi_body

uuid=$(router_get_uuid "$path") || return 1
[[ -n "$uuid" && "$uuid" != "null" ]] || return 1

jsonapi_body=$(http_get "${DRUPAL_ADDRESS}/jsonapi/node/${content_type}/${uuid}") || return 1
[[ "$(jq -r '.data.id' <<<"$jsonapi_body")" == "$uuid" ]]
}

next_build() (
set -e

cd ./next
# silence nvm install stderr which outputs when the node version is already installed
nvm install 2>/dev/null
nvm use

# Build sample pages per content type (configurable via EXPORT_TEST_PAGES_PER_CONTENT_TYPE)
export SSG_CHERRY_PICKED_PATHS
SSG_CHERRY_PICKED_PATHS=$(build_sample_paths)
echo "Building paths: ${SSG_CHERRY_PICKED_PATHS}"
APP_ENV="${APP_ENV}" BUILD_OPTION=static yarn export --no-USE_REDIS
)

echo "Testing against: ${DRUPAL_ADDRESS}"
echo ""

# Router tests
run_test "Router: VAMC system page" router_has_uuid "/boston-health-care"
run_test "Router: Event listing" router_has_uuid "/boston-health-care/events"
run_test "Router: Story listing" router_has_uuid "/boston-health-care/stories"
run_test "Router: Locations listing" router_has_uuid "/boston-health-care/locations"

# JSON:API tests
run_test "JSON:API: VAMC system nodes" jsonapi_has_data "/jsonapi/node/health_care_region_page?page[limit]=1"
run_test "JSON:API: Event listing nodes" jsonapi_has_data "/jsonapi/node/event_listing?page[limit]=1"
run_test "JSON:API: Story listing nodes" jsonapi_has_data "/jsonapi/node/story_listing?page[limit]=1"

# Full flow tests
run_test "Flow: VAMC system" router_to_jsonapi_flow "/boston-health-care" "health_care_region_page"
run_test "Flow: Event listing" router_to_jsonapi_flow "/boston-health-care/events" "event_listing"

# Next-build test (ensure next-build is not broken in this environment)
run_test "Build: next-build" next_build

echo ""
echo "Failures: $FAILURES"

exit $((FAILURES > 0))
Loading