-
Notifications
You must be signed in to change notification settings - Fork 0
CSS Phase C sprint C1: shared testimonials component (7 consumers) + fast-gate test infrastructure #371
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
CSS Phase C sprint C1: shared testimonials component (7 consumers) + fast-gate test infrastructure #371
Changes from all commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
07e58db
test(visual): bin/qtest scoped visual gate - affected-pages-only runs…
6d43944
fix(qtest): default to --changed; validate page keys before paying fo…
782507e
refactor(css): C1.1 components/testimonials.css extracted; about-us s…
13e374c
docs(css): C1.1 progress + method corrections - slice-member wiring, …
07b2652
refactor(css): C1.1 clients swapped to components/testimonials.css (2/7)
4bd7275
refactor(css): C1.1 services swapped to components/testimonials.css (…
6f1f7c5
refactor(css): C1.1 single-use-cases swapped to components/testimonia…
a86098a
refactor(css): C1.1 single-service swapped to components/testimonials…
aa74a5b
refactor(css): use-cases @imports converted to slice members (C1.1 pr…
7162f20
test(visual): qtest rewritten in Ruby - 90s wall budget + random rota…
dd35c07
refactor(css): C1.1 use-cases swapped to components/testimonials.css …
90e00d2
refactor(css): delete dead homepage swiper-svg 24px rule (C1.1 prep, 7a)
50d67db
fix(qtest): sample random extras per test file - cross-file name batc…
80981da
refactor(css): C1.1 homepage swapped to components/testimonials.css (…
1dba353
upgrades postcss
9bac6a9
test: churn-based test:critical subset + qtest fixtures guard + bin/d…
5a11cc1
refactor(css): delete 9 dead post-prelude @imports (audit of the sile…
c2e2691
test(css): bin/css-split - Ruby extraction tool for C1 component sprints
b4d3202
docs(css): tracker - C1.1 shipped, pre-existing test:all failures logged
e0460e1
refactor(css): C1.2 components/cta-banner.css extracted; about-us swa…
c837cbe
refactor(css): C1.2 homepage swapped to components/cta-banner.css (2/6)
43c3b2c
refactor(css): C1.2 services, single-service, single-use-cases, use-c…
b7637c8
refactor(css): C1.3 components/header-cta.css - bf72bba trio extracte…
f1081dc
refactor(css): delete 5 never-shipped c-pp component files (2,623 lines)
45e2809
test: delete flaky, redundant careers_page component tests + their ba…
93c8e19
fix(css-split): span-cut remainder mode - untouched regions stay byte…
ebeee56
test(visual): mask the Gravity Forms region on free_consultation - bi…
d7ebc83
docs(css): C1 complete - tracker C1.2-C1.5 evidence + ownership map c…
e696847
fix: address CodeRabbit review - css-split hardening + duplicate decl…
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,180 @@ | ||
| #!/usr/bin/env ruby | ||
| # frozen_string_literal: true | ||
|
|
||
| # css-split — C1 shared-component extraction tool (spec: Phase C, sprint C1). | ||
| # | ||
| # Splits a page CSS file into component-member rules and a remainder, by a | ||
| # normalized-key list, preserving original formatting, order, and @media | ||
| # grouping. Replaces the scratchpad awk pipeline that caused two self-inflicted | ||
| # defects in C1.1 (wrong-line dedupe, truncated header comment). | ||
| # | ||
| # Usage: | ||
| # bin/css-split keys KEYSFILE PAGE.css # print normalized keys of matching rules | ||
| # bin/css-split component KEYSFILE PAGE.css # print matching rules (original text) | ||
| # bin/css-split remainder KEYSFILE PAGE.css # print file minus matching rules | ||
| # bin/css-split extract PATTERN PAGE.css # print normalized keys of rules whose | ||
| # # selector matches PATTERN (regex) | ||
| # | ||
| # Key format (one per line): [media||]selector{decls} with ALL whitespace | ||
| # removed and the long system-ui font stack replaced by var(--font-system-ui). | ||
| # Built-in checks: brace balance and comment closure on every input; the | ||
| # remainder writer refuses to drop rules not present in KEYSFILE. | ||
|
|
||
| require "set" | ||
|
|
||
| FONT_STACK = 'system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", ' \ | ||
| '"Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", ' \ | ||
| '"Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"' | ||
|
|
||
| Rule = Struct.new(:media, :text, :key, :from, :to, :shell_from, :shell_to) | ||
|
|
||
| # Canonicalize while KEEPING descendant-combinator spaces: collapsing all | ||
| # whitespace would collide ".a .b" with ".a.b" and silently extract both. | ||
| def canonical(str) | ||
| str.gsub(/\s+/, " ").gsub(/\s*([{}:;,>~+()])\s*/, '\1').strip | ||
| end | ||
|
|
||
| def normalize(text, media) | ||
| k = canonical(text.gsub(%r{/\*.*?\*/}m, "")) | ||
| k = k.gsub(canonical(FONT_STACK), "var(--font-system-ui)") | ||
| media ? "#{canonical(media)}||#{k}" : k | ||
| end | ||
|
|
||
| # Returns [rules, plains] where rules are Rule structs in document order and | ||
| # plains are top-level non-rule statements (@import/@charset) with positions. | ||
| def parse(css, path) | ||
| stripped = css.gsub(%r{/\*.*?\*/}m, "").gsub(/"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'/m, "") | ||
| depth = stripped.count("{") - stripped.count("}") | ||
| abort "css-split: #{path}: unbalanced braces (#{depth})" unless depth.zero? | ||
| abort "css-split: #{path}: unclosed comment" if stripped.include?("/*") | ||
|
|
||
| rules = [] | ||
| i = 0 | ||
| n = css.length | ||
| start = 0 | ||
| depth = 0 | ||
| media = nil | ||
| mstart = nil | ||
| while i < n | ||
| c = css[i] | ||
| if c == "/" && css[i + 1] == "*" | ||
| close = css.index("*/", i + 2) | ||
| i = close ? close + 2 : n | ||
| next | ||
| end | ||
| if c == '"' || c == "'" | ||
| # skip string literals so braces/comment-openers inside them are inert | ||
| j = i + 1 | ||
| j += (css[j] == "\\" ? 2 : 1) while j < n && css[j] != c | ||
| i = j + 1 | ||
| next | ||
| end | ||
| case c | ||
| when ";" | ||
| if depth.zero? && media.nil? | ||
| rules << Rule.new(nil, css[start..i], :plain, start, i) | ||
| start = i + 1 | ||
| end | ||
| when "{" | ||
| if depth.zero? | ||
| head = css[start...i].gsub(%r{/\*.*?\*/}m, "").gsub(/\s+/, " ").strip | ||
| if media.nil? && head.start_with?("@media") | ||
| media = head | ||
| mstart = start | ||
| start = i + 1 | ||
| i += 1 | ||
| next | ||
| end | ||
| end | ||
| depth += 1 | ||
| when "}" | ||
| if depth.zero? && media | ||
| rules.reverse_each do |r| | ||
| break unless r.media == media && r.shell_to.nil? | ||
|
|
||
| r.shell_from = mstart | ||
| r.shell_to = i | ||
| end | ||
| media = nil | ||
| start = i + 1 | ||
| i += 1 | ||
| next | ||
| end | ||
| depth -= 1 | ||
| if depth.zero? | ||
| text = css[start..i] | ||
| rules << Rule.new(media, text, normalize(text, media), start, i) | ||
| start = i + 1 | ||
| end | ||
| end | ||
| i += 1 | ||
| end | ||
| rules | ||
| end | ||
|
|
||
| mode, keys_arg, path = ARGV | ||
| abort "css-split: usage: bin/css-split keys|component|remainder|extract ARG PAGE.css" unless mode && keys_arg && path | ||
|
|
||
| css = File.read(path) | ||
| rules = parse(css, path) | ||
|
|
||
| case mode | ||
| when "extract" | ||
| pat = Regexp.new(keys_arg) | ||
| rules.each do |r| | ||
| next if r.key == :plain | ||
|
|
||
| sel = r.text[/\A[^{]*/]&.gsub(%r{/\*.*?\*/}m, "") | ||
| puts r.key if sel&.match?(pat) | ||
| end | ||
| when "keys", "component", "remainder" | ||
| keyset = File.readlines(keys_arg, chomp: true).reject(&:empty?).to_set | ||
| open_media = nil | ||
| rules.each do |r| | ||
| member = r.key != :plain && keyset.include?(r.key) | ||
| case mode | ||
| when "keys" | ||
| puts r.key if member | ||
| when "component" | ||
| next unless member | ||
|
|
||
| t = r.text.gsub(FONT_STACK, "var(--font-system-ui)") | ||
| if r.media != open_media | ||
| print "}\n" if open_media | ||
| print "#{r.media} {\n" if r.media | ||
| open_media = r.media | ||
| end | ||
| print t | ||
| print "\n" | ||
| end | ||
| end | ||
| print "}\n" if open_media && mode == "component" | ||
|
|
||
| if mode == "remainder" | ||
| # cut removed spans from the ORIGINAL text (untouched regions stay | ||
| # byte-identical); a media shell loses its whole span only when ALL | ||
| # its inner rules are removed | ||
| cuts = [] | ||
| rules.group_by { |r| r.shell_from }.each do |shell, group| | ||
| removed = group.select { |r| r.key != :plain && keyset.include?(r.key) } | ||
| next if removed.empty? | ||
|
|
||
| if shell && removed.size == group.size | ||
| cuts << [shell, group.first.shell_to] | ||
| else | ||
| removed.each { |r| cuts << [r.from, r.to] } | ||
| end | ||
| end | ||
| out = +"" | ||
| pos = 0 | ||
| cuts.sort_by(&:first).each do |from, to| | ||
| out << css[pos...from] | ||
| pos = to + 1 | ||
| pos += 1 while css[pos] == "\n" && css[pos, 2] == "\n\n" # collapse the gap a little | ||
| end | ||
| out << css[pos..] | ||
| print out | ||
| end | ||
| else | ||
| abort "css-split: unknown mode #{mode}" | ||
| end | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| #!/usr/bin/env ruby | ||
| # frozen_string_literal: true | ||
|
|
||
| # dtest-all — the WHOLE test suite (rake test:all) in Docker, detached, | ||
| # so local work can continue while it runs. | ||
| # | ||
| # Isolation: builds a dedicated snapshot to _dest/public-dtest-all (never | ||
| # touched by bin/test/bin/dtest/qtest), then runs the suite in a background | ||
| # container against it. Keep editing freely once "container started" prints - | ||
| # the only shared writes are Linux screenshot baselines | ||
| # (test/fixtures/screenshots/linux/), which macOS-side work never touches. | ||
| # Note: test *code* is read from the live mount at container boot, so avoid | ||
| # editing test/**/*.rb during the first seconds of the run. | ||
| # | ||
| # Usage: | ||
| # bin/dtest-all # start; prints log path + container id | ||
| # tail -f tmp/dtest-all.log | ||
|
|
||
| DEST = "_dest/public-dtest-all" | ||
| LOG = "tmp/dtest-all.log" | ||
|
|
||
| puts "dtest-all: building snapshot to #{DEST} ..." | ||
| system("hugo", "--noBuildLock", "--buildDrafts", "--environment", "production", | ||
| "--destination", DEST, "--logLevel", "warn", | ||
| "--baseURL", "http://localhost:1314") || abort("dtest-all: hugo build failed") | ||
|
|
||
| require "fileutils" | ||
| FileUtils.mkdir_p("tmp") | ||
|
|
||
| cmd = [ | ||
| "bin/dc", "run", "--rm", "--detach", "--quiet-pull", "--remove-orphans", | ||
| "--env", "PRECOMPILED_ASSETS=true", | ||
| "--env", "TEST_SERVER_PORT=1314", | ||
| "--env", "HUGO_DEFAULT_PATH=#{DEST}", | ||
| "t", "sh", "-c", "bin/rake test:all > #{LOG} 2>&1; echo EXIT=$? >> #{LOG}" | ||
| ] | ||
| system(*cmd) || abort("dtest-all: failed to start container") | ||
|
|
||
| puts "dtest-all: started (docker ps | grep jtcom-t)" | ||
| puts "dtest-all: follow with tail -f #{LOG}" | ||
| puts "dtest-all: last line will read EXIT=0 on success" |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.