| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package controller |
| |
| import ( |
| "strings" |
| "testing" |
| |
| "github.com/apache/answer/ui" |
| "github.com/stretchr/testify/require" |
| ) |
| |
| // GetStyle scrapes the script and stylesheet paths out of the built |
| // index.html and every server-rendered page reuses them. The scrape is |
| // coupled to the exact attribute order and attribute set that the frontend |
| // build tool writes into those tags, and nothing in the system reports a |
| // mismatch: the frontend build still succeeds, the dev server still works, |
| // the binary still compiles, and the server-rendered pages simply come back |
| // with no script tags and no stylesheet. |
| // |
| // Assert the coupling directly so a change to the emitted tag shape fails |
| // here instead of shipping. |
| func TestGetStyleResolvesBuiltAssets(t *testing.T) { |
| const builtIndexPath = "build/index.html" |
| |
| raw, err := ui.Build.ReadFile(builtIndexPath) |
| if err != nil { |
| t.Skipf("no frontend build embedded at %s; build the frontend and re-run: %v", builtIndexPath, err) |
| } |
| |
| scripts, css := GetStyle() |
| |
| require.NotEmpty(t, scripts, |
| "no script sources parsed out of %s; server-rendered pages would load without any JavaScript", builtIndexPath) |
| for i, src := range scripts { |
| require.NotEmpty(t, src, "script source %d parsed out of %s is empty", i, builtIndexPath) |
| } |
| |
| require.NotEmpty(t, css, |
| "no stylesheet href parsed out of %s; server-rendered pages would load unstyled", builtIndexPath) |
| for i, href := range css { |
| require.NotEmpty(t, href, |
| "stylesheet href %d parsed out of %s is empty; server-rendered pages would load unstyled", i, builtIndexPath) |
| } |
| |
| // Finding every stylesheet matters as much as finding one. The build emits |
| // more than a single entry stylesheet, and a parser that stopped at the |
| // first one would still satisfy every assertion above while half the page's |
| // CSS silently stopped loading. That regression has happened once already. |
| // |
| // Count them again by a deliberately different and cruder method than the |
| // parser uses, so the two have to agree. It is a lower bound: a build that |
| // quotes attributes differently drives this to zero and the comparison |
| // simply stops constraining, which is why it supplements the assertions |
| // above rather than replacing them. |
| declared := strings.Count(string(raw), `rel="stylesheet"`) |
| require.GreaterOrEqual(t, len(css), declared, |
| "%s declares at least %d stylesheets but only %d were parsed out of it; "+ |
| "server-rendered pages would load missing part of their CSS", |
| builtIndexPath, declared, len(css)) |
| } |