Merge branch 'main' into mobile/bridge-components
* main:
Fix that caching should be disabled in dev by default. References 3cf841d463.
Revert "Add CJK (Chinese, Japanese, Korean) search support"
Account for new button sizes on mobile
Bump filter z-index when filters are open
Use a dedicated resource for showing cards in draft mode
Refactor popup.css styles for focus state
Add Segoe UI Variable Fizzy font face configuration comments
Update @layer name to `base` in font-face definition for "Segoe UI Variable Fizzy"
Fix font-face `src` reference for "Segoe UI Variable Fizzy" to correct local font lookup
Adjust font-weight range for "Segoe UI Variable Fizzy" to include 800-900
Update --font-sans and related font-face references to "Segoe UI Variable Fizzy" for consistency
Add "Segoe UI fizzy" to --font-sans and define custom font faces
Update --font-sans to include "Segoe UI Variable" for improved text rendering
Add margin to details inside popup styles
This commit is contained in:
@@ -10,7 +10,7 @@
|
|||||||
--block-space-double: calc(var(--block-space) * 2);
|
--block-space-double: calc(var(--block-space) * 2);
|
||||||
|
|
||||||
/* Text */
|
/* Text */
|
||||||
--font-sans: "Adwaita Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
|
--font-sans: "Adwaita Sans", -apple-system, BlinkMacSystemFont, "Segoe UI Variable Fizzy", "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
|
||||||
--font-serif: ui-serif, serif;
|
--font-serif: ui-serif, serif;
|
||||||
--font-mono: ui-monospace, monospace;
|
--font-mono: ui-monospace, monospace;
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
--comment-padding-block: var(--block-space-half);
|
--comment-padding-block: var(--block-space-half);
|
||||||
--comment-padding-inline: var(--inline-space-double);
|
--comment-padding-inline: var(--inline-space-double);
|
||||||
--comment-max: 70ch;
|
--comment-max: 70ch;
|
||||||
--reaction-size: 1.6875rem;
|
--reaction-size: 2rem;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -15,6 +15,10 @@
|
|||||||
@media (min-width: 160ch) {
|
@media (min-width: 160ch) {
|
||||||
padding-inline: var(--tray-size);
|
padding-inline: var(--tray-size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (min-width: 640px) {
|
||||||
|
--reaction-size: 1.6875rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.comments__subscribers {
|
.comments__subscribers {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
--input-background: var(--color-canvas);
|
--input-background: var(--color-canvas);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:has(dialog[open]),
|
||||||
&:has([data-controller~="tooltip"]:hover) {
|
&:has([data-controller~="tooltip"]:hover) {
|
||||||
z-index: calc(var(--z-nav) + 1);
|
z-index: calc(var(--z-nav) + 1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
@layer base {
|
||||||
|
/*
|
||||||
|
Segoe UI Variable Fizzy font face configuration.
|
||||||
|
|
||||||
|
1. Segoe UI Variable (Weights 100-700):
|
||||||
|
Leverages variable font features to:
|
||||||
|
- Automatically adjust Weight (wght) dynamically within the 100-700 range.
|
||||||
|
- Automatically manage Optical Size (opsz) based on font-size.
|
||||||
|
|
||||||
|
2. Segoe UI Black (Weights 800-900):
|
||||||
|
Used as a fallback because Segoe UI Variable does not natively support 900 weight.
|
||||||
|
This ensures a consistent bold experience across all weights.
|
||||||
|
*/
|
||||||
|
@font-face {
|
||||||
|
font-family: "Segoe UI Variable Fizzy";
|
||||||
|
src: local("Segoe UI Variable");
|
||||||
|
font-weight: 100 700;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Segoe UI Variable Fizzy";
|
||||||
|
src: local("Segoe UI Variable");
|
||||||
|
font-weight: 100 700;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Segoe UI Variable Fizzy";
|
||||||
|
src: local("Segoe UI Black");
|
||||||
|
font-weight: 800 900;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Segoe UI Variable Fizzy";
|
||||||
|
src: local("Segoe UI Black Italic");
|
||||||
|
font-weight: 800 900;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -155,6 +155,10 @@
|
|||||||
max-inline-size: 100%;
|
max-inline-size: 100%;
|
||||||
padding: var(--inline-space-half) var(--popup-item-padding-inline);
|
padding: var(--inline-space-half) var(--popup-item-padding-inline);
|
||||||
text-align: start;
|
text-align: start;
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup__icon {
|
.popup__icon {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: var(--inline-space-half);
|
gap: var(--inline-space-half);
|
||||||
inline-size: 100%;
|
inline-size: 100%;
|
||||||
margin-block-start: calc(var(--block-space-half) / 2);
|
margin-block-start: var(--block-space-half);
|
||||||
margin-inline: calc(var(--column-gap) / -1);
|
margin-inline: calc(var(--column-gap) / -1);
|
||||||
|
|
||||||
&:has([open]) {
|
&:has([open]) {
|
||||||
@@ -44,6 +44,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.reactions__trigger {
|
.reactions__trigger {
|
||||||
|
--btn-size: var(--reaction-size);
|
||||||
--btn-border-color: var(--reaction-border-color);
|
--btn-border-color: var(--reaction-border-color);
|
||||||
|
|
||||||
img {
|
img {
|
||||||
@@ -115,11 +116,13 @@
|
|||||||
|
|
||||||
/* Make the avatar and delete buttons fit nicely within the reaction */
|
/* Make the avatar and delete buttons fit nicely within the reaction */
|
||||||
.reaction__avatar,
|
.reaction__avatar,
|
||||||
|
.reaction__avatar .avatar,
|
||||||
.reaction__form-label,
|
.reaction__form-label,
|
||||||
.reaction form {
|
.reaction form {
|
||||||
block-size: var(--btn-size);
|
block-size: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.reaction__delete {
|
.reaction__delete {
|
||||||
display: none;
|
display: none;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
class Cards::DraftsController < ApplicationController
|
||||||
|
include CardScoped
|
||||||
|
|
||||||
|
before_action :redirect_if_published
|
||||||
|
|
||||||
|
def show
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def redirect_if_published
|
||||||
|
redirect_to @card unless @card.drafted?
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -5,7 +5,8 @@ class Cards::PublishesController < ApplicationController
|
|||||||
@card.publish
|
@card.publish
|
||||||
|
|
||||||
if add_another_param?
|
if add_another_param?
|
||||||
redirect_to @board.cards.create!, notice: "Card added"
|
card = @board.cards.create!(status: :drafted)
|
||||||
|
redirect_to card_draft_path(card), notice: "Card added"
|
||||||
else
|
else
|
||||||
redirect_to @card.board
|
redirect_to @card.board
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ class CardsController < ApplicationController
|
|||||||
|
|
||||||
before_action :set_board, only: %i[ create ]
|
before_action :set_board, only: %i[ create ]
|
||||||
before_action :set_card, only: %i[ show edit update destroy ]
|
before_action :set_card, only: %i[ show edit update destroy ]
|
||||||
|
before_action :redirect_if_drafted, only: :show
|
||||||
before_action :ensure_permission_to_administer_card, only: %i[ destroy ]
|
before_action :ensure_permission_to_administer_card, only: %i[ destroy ]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@@ -13,7 +14,7 @@ class CardsController < ApplicationController
|
|||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html do
|
format.html do
|
||||||
card = Current.user.draft_new_card_in(@board)
|
card = Current.user.draft_new_card_in(@board)
|
||||||
redirect_to card
|
redirect_to card_draft_path(card)
|
||||||
end
|
end
|
||||||
|
|
||||||
format.json do
|
format.json do
|
||||||
@@ -56,6 +57,10 @@ class CardsController < ApplicationController
|
|||||||
@card = Current.user.accessible_cards.find_by!(number: params[:id])
|
@card = Current.user.accessible_cards.find_by!(number: params[:id])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def redirect_if_drafted
|
||||||
|
redirect_to card_draft_path(@card) if @card.drafted?
|
||||||
|
end
|
||||||
|
|
||||||
def ensure_permission_to_administer_card
|
def ensure_permission_to_administer_card
|
||||||
head :forbidden unless Current.user.can_administer_card?(@card)
|
head :forbidden unless Current.user.can_administer_card?(@card)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
module Search
|
module Search
|
||||||
CJK_PATTERN = /\p{Han}|\p{Hiragana}|\p{Katakana}|\p{Hangul}/
|
|
||||||
|
|
||||||
def self.table_name_prefix
|
def self.table_name_prefix
|
||||||
"search_"
|
"search_"
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -13,14 +13,8 @@ class Search::Highlighter
|
|||||||
result = text.dup
|
result = text.dup
|
||||||
|
|
||||||
terms.each do |term|
|
terms.each do |term|
|
||||||
if term.match?(Search::CJK_PATTERN)
|
result.gsub!(/\b(#{Regexp.escape(term)}\w*)\b/i) do |match|
|
||||||
result.gsub!(/(#{Regexp.escape(term)})/i) do |match|
|
"#{OPENING_MARK}#{match}#{CLOSING_MARK}"
|
||||||
"#{OPENING_MARK}#{match}#{CLOSING_MARK}"
|
|
||||||
end
|
|
||||||
else
|
|
||||||
result.gsub!(/\b(#{Regexp.escape(term)}\w*)\b/i) do |match|
|
|
||||||
"#{OPENING_MARK}#{match}#{CLOSING_MARK}"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class Search::Query < ApplicationRecord
|
|||||||
end
|
end
|
||||||
|
|
||||||
def remove_invalid_search_characters(terms)
|
def remove_invalid_search_characters(terms)
|
||||||
terms.gsub(/[^\p{L}\p{N}_"]/, " ")
|
terms.gsub(/[^\w"]/, " ")
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove_unbalanced_quotes(terms)
|
def remove_unbalanced_quotes(terms)
|
||||||
|
|||||||
@@ -8,10 +8,9 @@ module Search::Record::SQLite
|
|||||||
has_one :search_records_fts, -> { with_rowid },
|
has_one :search_records_fts, -> { with_rowid },
|
||||||
class_name: "Search::Record::SQLite::Fts", foreign_key: :rowid, primary_key: :id, dependent: :destroy
|
class_name: "Search::Record::SQLite::Fts", foreign_key: :rowid, primary_key: :id, dependent: :destroy
|
||||||
|
|
||||||
before_save :stem_content
|
|
||||||
after_save :upsert_to_fts5_table
|
after_save :upsert_to_fts5_table
|
||||||
|
|
||||||
scope :matching, ->(query, account_id) { joins(:search_records_fts).where("search_records_fts MATCH ?", Search::Stemmer.stem(query.to_s)) }
|
scope :matching, ->(query, account_id) { joins(:search_records_fts).where("search_records_fts MATCH ?", query) }
|
||||||
end
|
end
|
||||||
|
|
||||||
class_methods do
|
class_methods do
|
||||||
@@ -43,11 +42,6 @@ module Search::Record::SQLite
|
|||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def stem_content
|
|
||||||
self.title = Search::Stemmer.stem(title) if title_changed?
|
|
||||||
self.content = Search::Stemmer.stem(content) if content_changed?
|
|
||||||
end
|
|
||||||
|
|
||||||
def escape_fts_highlight(html)
|
def escape_fts_highlight(html)
|
||||||
return nil unless html.present?
|
return nil unless html.present?
|
||||||
|
|
||||||
|
|||||||
@@ -5,43 +5,9 @@ module Search::Stemmer
|
|||||||
|
|
||||||
def stem(value)
|
def stem(value)
|
||||||
if value.present?
|
if value.present?
|
||||||
tokenize(value).join(" ")
|
value.gsub(/[^\w\s]/, "").split(/\s+/).map { |word| STEMMER.stem(word.downcase) }.join(" ")
|
||||||
else
|
else
|
||||||
value
|
value
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
def tokenize(value)
|
|
||||||
tokens = []
|
|
||||||
current_word = +""
|
|
||||||
|
|
||||||
value.each_char do |char|
|
|
||||||
if cjk_character?(char)
|
|
||||||
if current_word.present?
|
|
||||||
tokens << stem_word(current_word)
|
|
||||||
current_word = +""
|
|
||||||
end
|
|
||||||
tokens << char
|
|
||||||
elsif char =~ /[\p{L}\p{N}_]/
|
|
||||||
current_word << char
|
|
||||||
else
|
|
||||||
if current_word.present?
|
|
||||||
tokens << stem_word(current_word)
|
|
||||||
current_word = +""
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
tokens << stem_word(current_word) if current_word.present?
|
|
||||||
tokens
|
|
||||||
end
|
|
||||||
|
|
||||||
def cjk_character?(char)
|
|
||||||
char.match?(Search::CJK_PATTERN)
|
|
||||||
end
|
|
||||||
|
|
||||||
def stem_word(word)
|
|
||||||
STEMMER.stem(word.downcase)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -37,11 +37,5 @@
|
|||||||
|
|
||||||
<% if card.published? %>
|
<% if card.published? %>
|
||||||
<%= render "cards/container/footer/published", card: card %>
|
<%= render "cards/container/footer/published", card: card %>
|
||||||
<% elsif card.drafted? %>
|
|
||||||
<% if Fizzy.saas? %>
|
|
||||||
<%= render "cards/container/footer/saas/create", card: card %>
|
|
||||||
<% else %>
|
|
||||||
<%= render "cards/container/footer/create", card: card %>
|
|
||||||
<% end %>
|
|
||||||
<% end %>
|
<% end %>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<section id="<%= dom_id(card, :card_container) %>" class="card-perma" style="--card-color: <%= card.color %>;">
|
||||||
|
<% cache card do %>
|
||||||
|
<div class="card-perma__actions card-perma__actions--left">
|
||||||
|
<%= render "cards/container/image", card: card %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-perma__bg">
|
||||||
|
<%= card_article_tag card, class: "card" do %>
|
||||||
|
<header class="card__header">
|
||||||
|
<%= render "cards/display/perma/board", card: card %>
|
||||||
|
<%= render "cards/display/perma/tags", card: card %>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="card__body justify-space-between">
|
||||||
|
<div class="card__content">
|
||||||
|
<%= render "cards/container/content", card: card %>
|
||||||
|
<%= render "cards/display/perma/steps", card: card %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="card__footer full-width flex align-start gap">
|
||||||
|
<%= render "cards/display/perma/meta", card: card %>
|
||||||
|
<%= render "cards/display/perma/background", card: card %>
|
||||||
|
</footer>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<% if Fizzy.saas? %>
|
||||||
|
<%= render "cards/container/footer/saas/create", card: card %>
|
||||||
|
<% else %>
|
||||||
|
<%= render "cards/container/footer/create", card: card %>
|
||||||
|
<% end %>
|
||||||
|
</section>
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<% @page_title = @card.title %>
|
||||||
|
<% @header_class = "header--card" %>
|
||||||
|
|
||||||
|
<% content_for :header do %>
|
||||||
|
<div class="header__actions header__actions--start">
|
||||||
|
<%= link_back_to_board(@card.board) %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<div data-controller="beacon lightbox" data-beacon-url-value="<%= card_reading_path(@card) %>">
|
||||||
|
<%= render "cards/drafts/container", card: @card %>
|
||||||
|
|
||||||
|
<%= render "layouts/lightbox" do %>
|
||||||
|
<%= button_to_remove_card_image(@card) if @card.image.attached? %>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
<div data-controller="beacon lightbox" data-beacon-url-value="<%= card_reading_path(@card) %>">
|
<div data-controller="beacon lightbox" data-beacon-url-value="<%= card_reading_path(@card) %>">
|
||||||
<%= render "cards/container", card: @card %>
|
<%= render "cards/container", card: @card %>
|
||||||
<%= render "cards/messages", card: @card unless @card.drafted? %>
|
<%= render "cards/messages", card: @card %>
|
||||||
|
|
||||||
<%= render "layouts/lightbox" do %>
|
<%= render "layouts/lightbox" do %>
|
||||||
<%= button_to_remove_card_image(@card) if @card.image.attached? %>
|
<%= button_to_remove_card_image(@card) if @card.image.attached? %>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ Rails.application.configure do
|
|||||||
config.cache_store = :memory_store
|
config.cache_store = :memory_store
|
||||||
config.public_file_server.headers = { "Cache-Control" => "public, max-age=#{2.days.to_i}" }
|
config.public_file_server.headers = { "Cache-Control" => "public, max-age=#{2.days.to_i}" }
|
||||||
else
|
else
|
||||||
config.action_controller.perform_caching = true
|
config.action_controller.perform_caching = false
|
||||||
|
|
||||||
config.cache_store = :null_store
|
config.cache_store = :null_store
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ Rails.application.routes.draw do
|
|||||||
|
|
||||||
resources :cards do
|
resources :cards do
|
||||||
scope module: :cards do
|
scope module: :cards do
|
||||||
|
resource :draft, only: :show
|
||||||
resource :board
|
resource :board
|
||||||
resource :closure
|
resource :closure
|
||||||
resource :column
|
resource :column
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ class Account::Subscriptions::CardCreationTest < ActionDispatch::IntegrationTest
|
|||||||
test "admin sees nearing card limit notice" do
|
test "admin sees nearing card limit notice" do
|
||||||
accounts(:initech).update_column(:cards_count, 950)
|
accounts(:initech).update_column(:cards_count, 950)
|
||||||
|
|
||||||
get card_path(cards(:unfinished_thoughts), script_name: accounts(:initech).slug)
|
get card_draft_path(cards(:unfinished_thoughts), script_name: accounts(:initech).slug)
|
||||||
|
|
||||||
assert_response :success
|
assert_response :success
|
||||||
assert_match /upgrade to unlimited/i, response.body
|
assert_match /upgrade to unlimited/i, response.body
|
||||||
@@ -19,7 +19,7 @@ class Account::Subscriptions::CardCreationTest < ActionDispatch::IntegrationTest
|
|||||||
test "admin sees nearing storage limit notice" do
|
test "admin sees nearing storage limit notice" do
|
||||||
Account.any_instance.stubs(:bytes_used).returns(600.megabytes)
|
Account.any_instance.stubs(:bytes_used).returns(600.megabytes)
|
||||||
|
|
||||||
get card_path(cards(:unfinished_thoughts), script_name: accounts(:initech).slug)
|
get card_draft_path(cards(:unfinished_thoughts), script_name: accounts(:initech).slug)
|
||||||
|
|
||||||
assert_response :success
|
assert_response :success
|
||||||
assert_match /upgrade to get more/i, response.body
|
assert_match /upgrade to get more/i, response.body
|
||||||
@@ -30,7 +30,7 @@ class Account::Subscriptions::CardCreationTest < ActionDispatch::IntegrationTest
|
|||||||
test "admin sees exceeding card limit notice" do
|
test "admin sees exceeding card limit notice" do
|
||||||
accounts(:initech).update_column(:cards_count, 1001)
|
accounts(:initech).update_column(:cards_count, 1001)
|
||||||
|
|
||||||
get card_path(cards(:unfinished_thoughts), script_name: accounts(:initech).slug)
|
get card_draft_path(cards(:unfinished_thoughts), script_name: accounts(:initech).slug)
|
||||||
|
|
||||||
assert_response :success
|
assert_response :success
|
||||||
assert_match /you’ve used your.*free cards/i, response.body
|
assert_match /you’ve used your.*free cards/i, response.body
|
||||||
@@ -39,7 +39,7 @@ class Account::Subscriptions::CardCreationTest < ActionDispatch::IntegrationTest
|
|||||||
test "admin sees exceeding storage limit notice" do
|
test "admin sees exceeding storage limit notice" do
|
||||||
Account.any_instance.stubs(:bytes_used).returns(1.1.gigabytes)
|
Account.any_instance.stubs(:bytes_used).returns(1.1.gigabytes)
|
||||||
|
|
||||||
get card_path(cards(:unfinished_thoughts), script_name: accounts(:initech).slug)
|
get card_draft_path(cards(:unfinished_thoughts), script_name: accounts(:initech).slug)
|
||||||
|
|
||||||
assert_response :success
|
assert_response :success
|
||||||
assert_match /you’ve run out of.*free storage/i, response.body
|
assert_match /you’ve run out of.*free storage/i, response.body
|
||||||
@@ -64,7 +64,7 @@ class Account::Subscriptions::CardCreationTest < ActionDispatch::IntegrationTest
|
|||||||
test "comped account under limits sees no notices" do
|
test "comped account under limits sees no notices" do
|
||||||
accounts(:initech).comp
|
accounts(:initech).comp
|
||||||
|
|
||||||
get card_path(cards(:unfinished_thoughts), script_name: accounts(:initech).slug)
|
get card_draft_path(cards(:unfinished_thoughts), script_name: accounts(:initech).slug)
|
||||||
|
|
||||||
assert_response :success
|
assert_response :success
|
||||||
assert_no_match /upgrade/i, response.body
|
assert_no_match /upgrade/i, response.body
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
class Cards::DraftsControllerTest < ActionDispatch::IntegrationTest
|
||||||
|
setup do
|
||||||
|
sign_in_as :kevin
|
||||||
|
end
|
||||||
|
|
||||||
|
test "show" do
|
||||||
|
card = boards(:writebook).cards.create!(creator: users(:kevin), status: :drafted)
|
||||||
|
|
||||||
|
get card_draft_path(card)
|
||||||
|
assert_response :success
|
||||||
|
end
|
||||||
|
|
||||||
|
test "show redirects to card when published" do
|
||||||
|
card = cards(:logo)
|
||||||
|
|
||||||
|
get card_draft_path(card)
|
||||||
|
assert_redirected_to card
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -28,6 +28,6 @@ class Cards::PublishesControllerTest < ActionDispatch::IntegrationTest
|
|||||||
|
|
||||||
new_card = Card.last
|
new_card = Card.last
|
||||||
assert new_card.drafted?
|
assert new_card.drafted?
|
||||||
assert_redirected_to new_card
|
assert_redirected_to card_draft_path(new_card)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class CardsControllerTest < ActionDispatch::IntegrationTest
|
|||||||
end
|
end
|
||||||
|
|
||||||
card = Card.last
|
card = Card.last
|
||||||
assert_redirected_to card
|
assert_redirected_to card_draft_path(card)
|
||||||
|
|
||||||
assert card.drafted?
|
assert card.drafted?
|
||||||
end
|
end
|
||||||
@@ -31,10 +31,17 @@ class CardsControllerTest < ActionDispatch::IntegrationTest
|
|||||||
|
|
||||||
assert_no_difference -> { Card.count } do
|
assert_no_difference -> { Card.count } do
|
||||||
post board_cards_path(boards(:writebook))
|
post board_cards_path(boards(:writebook))
|
||||||
assert_redirected_to draft
|
assert_redirected_to card_draft_path(draft)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "show redirects to draft when card is drafted" do
|
||||||
|
card = boards(:writebook).cards.create!(creator: users(:kevin), status: :drafted)
|
||||||
|
|
||||||
|
get card_path(card)
|
||||||
|
assert_redirected_to card_draft_path(card)
|
||||||
|
end
|
||||||
|
|
||||||
test "show" do
|
test "show" do
|
||||||
get card_path(cards(:logo))
|
get card_path(cards(:logo))
|
||||||
assert_response :success
|
assert_response :success
|
||||||
|
|||||||
@@ -82,34 +82,6 @@ class Search::HighlighterTest < ActiveSupport::TestCase
|
|||||||
assert_equal "<script>#{mark('test')}</script>", result
|
assert_equal "<script>#{mark('test')}</script>", result
|
||||||
end
|
end
|
||||||
|
|
||||||
test "highlight Chinese characters" do
|
|
||||||
highlighter = Search::Highlighter.new("测试")
|
|
||||||
result = highlighter.highlight("这是一个测试文本")
|
|
||||||
|
|
||||||
assert_equal "这是一个#{mark('测试')}文本", result
|
|
||||||
end
|
|
||||||
|
|
||||||
test "highlight Japanese characters" do
|
|
||||||
highlighter = Search::Highlighter.new("テスト")
|
|
||||||
result = highlighter.highlight("これはテストです")
|
|
||||||
|
|
||||||
assert_equal "これは#{mark('テスト')}です", result
|
|
||||||
end
|
|
||||||
|
|
||||||
test "highlight Korean characters" do
|
|
||||||
highlighter = Search::Highlighter.new("테스트")
|
|
||||||
result = highlighter.highlight("이것은 테스트입니다")
|
|
||||||
|
|
||||||
assert_equal "이것은 #{mark('테스트')}입니다", result
|
|
||||||
end
|
|
||||||
|
|
||||||
test "highlight mixed CJK and English" do
|
|
||||||
highlighter = Search::Highlighter.new("world 世界")
|
|
||||||
result = highlighter.highlight("hello world 你好世界")
|
|
||||||
|
|
||||||
assert_equal "hello #{mark('world')} 你好#{mark('世界')}", result
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
def mark(text)
|
def mark(text)
|
||||||
"#{Search::Highlighter::OPENING_MARK}#{text}#{Search::Highlighter::CLOSING_MARK}"
|
"#{Search::Highlighter::OPENING_MARK}#{text}#{Search::Highlighter::CLOSING_MARK}"
|
||||||
|
|||||||
@@ -1,57 +0,0 @@
|
|||||||
require "test_helper"
|
|
||||||
|
|
||||||
class Search::QueryTest < ActiveSupport::TestCase
|
|
||||||
setup do
|
|
||||||
@account = accounts(:"37s")
|
|
||||||
Current.account = @account
|
|
||||||
end
|
|
||||||
|
|
||||||
test "sanitize preserves ASCII words" do
|
|
||||||
query = build_query("hello world")
|
|
||||||
|
|
||||||
assert_equal "hello world", query.terms
|
|
||||||
end
|
|
||||||
|
|
||||||
test "sanitize preserves Chinese characters" do
|
|
||||||
query = build_query("测试文本")
|
|
||||||
|
|
||||||
assert_equal "测试文本", query.terms
|
|
||||||
end
|
|
||||||
|
|
||||||
test "sanitize preserves Japanese characters" do
|
|
||||||
query = build_query("テスト")
|
|
||||||
|
|
||||||
assert_equal "テスト", query.terms
|
|
||||||
end
|
|
||||||
|
|
||||||
test "sanitize preserves Korean characters" do
|
|
||||||
query = build_query("테스트")
|
|
||||||
|
|
||||||
assert_equal "테스트", query.terms
|
|
||||||
end
|
|
||||||
|
|
||||||
test "sanitize preserves mixed CJK and English" do
|
|
||||||
query = build_query("hello 世界 test")
|
|
||||||
|
|
||||||
assert_equal "hello 世界 test", query.terms
|
|
||||||
end
|
|
||||||
|
|
||||||
test "sanitize removes special characters but preserves CJK" do
|
|
||||||
query = build_query("测试@文本")
|
|
||||||
|
|
||||||
assert_equal "测试 文本", query.terms
|
|
||||||
end
|
|
||||||
|
|
||||||
test "sanitize preserves quoted phrases with CJK" do
|
|
||||||
query = build_query('"你好世界"')
|
|
||||||
|
|
||||||
assert_equal '"你好世界"', query.terms
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
def build_query(terms)
|
|
||||||
query = Search::Query.wrap(terms)
|
|
||||||
query.validate
|
|
||||||
query
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -12,40 +12,4 @@ class Search::StemmerTest < ActiveSupport::TestCase
|
|||||||
|
|
||||||
assert_equal "test run jump walk", result
|
assert_equal "test run jump walk", result
|
||||||
end
|
end
|
||||||
|
|
||||||
test "split Chinese characters for FTS indexing" do
|
|
||||||
result = Search::Stemmer.stem("测试")
|
|
||||||
|
|
||||||
assert_equal "测 试", result
|
|
||||||
end
|
|
||||||
|
|
||||||
test "split Japanese characters for FTS indexing" do
|
|
||||||
result = Search::Stemmer.stem("テスト")
|
|
||||||
|
|
||||||
assert_equal "テ ス ト", result
|
|
||||||
end
|
|
||||||
|
|
||||||
test "split Korean characters for FTS indexing" do
|
|
||||||
result = Search::Stemmer.stem("테스트")
|
|
||||||
|
|
||||||
assert_equal "테 스 트", result
|
|
||||||
end
|
|
||||||
|
|
||||||
test "mixed CJK and English" do
|
|
||||||
result = Search::Stemmer.stem("running 测试 test")
|
|
||||||
|
|
||||||
assert_equal "run 测 试 test", result
|
|
||||||
end
|
|
||||||
|
|
||||||
test "mixed CJK and English without spaces" do
|
|
||||||
result = Search::Stemmer.stem("hello世界test")
|
|
||||||
|
|
||||||
assert_equal "hello 世 界 test", result
|
|
||||||
end
|
|
||||||
|
|
||||||
test "CJK punctuation is treated as separator" do
|
|
||||||
result = Search::Stemmer.stem("你好。世界")
|
|
||||||
|
|
||||||
assert_equal "你 好 世 界", result
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user