mirror of
https://github.com/nexu-io/open-design.git
synced 2026-10-27 09:51:12 +01:00
fix(web): localize built-in skill edit as a user-override flow (#4793)
* i18n(web): add built-in skill user-override strings Add skillsOverrideCreate / skillsOverrideSave / skillsBuiltInOverrideWarning to the typed dictionary and every locale, ahead of wiring the built-in skill edit flow through them. * fix(web): localize the built-in skill override confirmation The built-in-skill edit warning was hardcoded English, so it stayed English under non-English locales even though the rest of the dialog followed the active language. Read it from the i18n dictionary instead. * fix(web): label built-in skill editing as create/save user override Editing a built-in skill silently produced a user override, but the affordances read "Edit" / "Save" — so the built-in → user type change was a surprise. The edit action, confirmation, and form heading now read "Create user override" and the submit reads "Save as user override". * test(web): lock built-in skill override wording across locales Cover the regression gap raised on #4793: assert the built-in skill edit flow renders the user-override i18n keys end to end — the edit tooltip, the confirmation warning, the confirm button, the form heading, and the submit button — in the default en locale and a translated zh-CN path, and that a user skill keeps the plain Edit/Save affordance with no override warning. Assertions compare against the locale dictionaries rather than hardcoded literals, so a copy tweak in a locale file stays green while reverting to the old Edit/Save keys (or a hardcoded-English warning) goes red.
This commit is contained in:
@@ -580,6 +580,10 @@ function SkillRow({
|
||||
const summaryName = localizeSkillName(locale, skill) || skill.id;
|
||||
const summaryDescription = localizeSkillDescription(locale, skill);
|
||||
const canDelete = skill.source === 'user';
|
||||
// Editing a built-in skill does not modify it in place — it writes a
|
||||
// user-owned shadow copy. Frame the affordance as creating a user override
|
||||
// so the built-in → user transition is not a surprise.
|
||||
const isBuiltIn = skill.source !== 'user';
|
||||
return (
|
||||
<div
|
||||
className={`skills-row${enabled ? '' : ' skills-row-disabled'}${
|
||||
@@ -651,7 +655,11 @@ function SkillRow({
|
||||
<Button
|
||||
size="icon"
|
||||
onClick={onStartEdit}
|
||||
title={t('settings.skillsEdit')}
|
||||
title={
|
||||
isBuiltIn
|
||||
? t('settings.skillsOverrideCreate')
|
||||
: t('settings.skillsEdit')
|
||||
}
|
||||
data-testid="skills-edit"
|
||||
>
|
||||
<Icon name="edit" size={13} />
|
||||
@@ -689,11 +697,7 @@ function SkillRow({
|
||||
role="alert"
|
||||
data-testid="skills-edit-builtin-warning"
|
||||
>
|
||||
<p>
|
||||
Editing this built-in skill creates a user override. The built-in
|
||||
entry will be hidden from the list until you delete the override.
|
||||
Continue?
|
||||
</p>
|
||||
<p>{t('settings.skillsBuiltInOverrideWarning')}</p>
|
||||
<div className="skills-edit-builtin-actions">
|
||||
<button
|
||||
type="button"
|
||||
@@ -709,7 +713,7 @@ function SkillRow({
|
||||
onClick={onConfirmBuiltInEdit}
|
||||
data-testid="skills-edit-builtin-confirm"
|
||||
>
|
||||
{t('settings.skillsEdit')}
|
||||
{t('settings.skillsOverrideCreate')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -759,13 +763,18 @@ function SkillRow({
|
||||
|
||||
{editing && draft ? (
|
||||
<SkillDraftForm
|
||||
heading={t('settings.skillsEdit')}
|
||||
heading={
|
||||
isBuiltIn
|
||||
? t('settings.skillsOverrideCreate')
|
||||
: t('settings.skillsEdit')
|
||||
}
|
||||
subheading={skill.id}
|
||||
draft={draft}
|
||||
setDraft={setDraft}
|
||||
error={draftError}
|
||||
saving={draftSaving}
|
||||
isEdit
|
||||
isBuiltInOverride={isBuiltIn}
|
||||
onCancel={onCancelEdit}
|
||||
onSubmit={onSubmitEdit}
|
||||
/>
|
||||
@@ -782,6 +791,8 @@ interface SkillDraftFormProps {
|
||||
error: string | null;
|
||||
saving: boolean;
|
||||
isEdit: boolean;
|
||||
/** Editing a built-in skill: the submit reads "Save as user override". */
|
||||
isBuiltInOverride?: boolean;
|
||||
onCancel: () => void;
|
||||
onSubmit: () => void;
|
||||
}
|
||||
@@ -794,6 +805,7 @@ function SkillDraftForm({
|
||||
error,
|
||||
saving,
|
||||
isEdit,
|
||||
isBuiltInOverride = false,
|
||||
onCancel,
|
||||
onSubmit,
|
||||
}: SkillDraftFormProps) {
|
||||
@@ -876,7 +888,9 @@ function SkillDraftForm({
|
||||
{saving
|
||||
? t('settings.skillsSaving')
|
||||
: isEdit
|
||||
? t('settings.skillsSave')
|
||||
? isBuiltInOverride
|
||||
? t('settings.skillsOverrideSave')
|
||||
: t('settings.skillsSave')
|
||||
: t('settings.skillsCreate')}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -3339,6 +3339,9 @@ export const ar: Dict = {
|
||||
'settings.skillsCreate': 'إنشاء',
|
||||
'settings.skillsSave': 'حفظ',
|
||||
'settings.skillsSaving': 'جاري الحفظ…',
|
||||
'settings.skillsOverrideCreate': 'إنشاء تجاوز المستخدم',
|
||||
'settings.skillsOverrideSave': 'حفظ كتجاوز المستخدم',
|
||||
'settings.skillsBuiltInOverrideWarning': 'يؤدي تعديل هذه المهارة المدمجة إلى إنشاء تجاوز خاص بالمستخدم. سيتم إخفاء العنصر المدمج من القائمة حتى تحذف التجاوز. هل تريد المتابعة؟',
|
||||
'settings.skillsFiles': 'الملفات',
|
||||
'settings.skillsNoFiles': 'لا توجد ملفات في مجلد هذه المهارة.',
|
||||
'settings.skillsNameRequired': 'اسم المهارة مطلوب.',
|
||||
|
||||
@@ -3339,6 +3339,9 @@ export const de: Dict = {
|
||||
'settings.skillsCreate': 'Erstellen',
|
||||
'settings.skillsSave': 'Speichern',
|
||||
'settings.skillsSaving': 'Speichern…',
|
||||
'settings.skillsOverrideCreate': 'Benutzer-Override erstellen',
|
||||
'settings.skillsOverrideSave': 'Als Benutzer-Override speichern',
|
||||
'settings.skillsBuiltInOverrideWarning': 'Das Bearbeiten dieser integrierten Fähigkeit erstellt einen Benutzer-Override. Der integrierte Eintrag wird aus der Liste ausgeblendet, bis du den Override löschst. Fortfahren?',
|
||||
'settings.skillsFiles': 'Dateien',
|
||||
'settings.skillsNoFiles': 'Keine Dateien in diesem Skill-Ordner.',
|
||||
'settings.skillsNameRequired': 'Skill-Name ist erforderlich.',
|
||||
|
||||
@@ -3377,6 +3377,9 @@ export const en: Dict = {
|
||||
'settings.skillsCreate': 'Create',
|
||||
'settings.skillsSave': 'Save',
|
||||
'settings.skillsSaving': 'Saving…',
|
||||
'settings.skillsOverrideCreate': 'Create user override',
|
||||
'settings.skillsOverrideSave': 'Save as user override',
|
||||
'settings.skillsBuiltInOverrideWarning': 'Editing this built-in skill creates a user override. The built-in entry will be hidden from the list until you delete the override. Continue?',
|
||||
'settings.skillsFiles': 'Files',
|
||||
'settings.skillsNoFiles': 'No files in this skill folder.',
|
||||
'settings.skillsNameRequired': 'Skill name is required.',
|
||||
|
||||
@@ -3339,6 +3339,9 @@ export const esES: Dict = {
|
||||
'settings.skillsCreate': 'Crear',
|
||||
'settings.skillsSave': 'Guardar',
|
||||
'settings.skillsSaving': 'Guardando…',
|
||||
'settings.skillsOverrideCreate': 'Crear anulación de usuario',
|
||||
'settings.skillsOverrideSave': 'Guardar como anulación de usuario',
|
||||
'settings.skillsBuiltInOverrideWarning': 'Editar esta habilidad integrada crea una anulación de usuario. La entrada integrada se ocultará de la lista hasta que elimines la anulación. ¿Continuar?',
|
||||
'settings.skillsFiles': 'Archivos',
|
||||
'settings.skillsNoFiles': 'No hay archivos en esta carpeta de habilidad.',
|
||||
'settings.skillsNameRequired': 'El nombre de la habilidad es obligatorio.',
|
||||
|
||||
@@ -3339,6 +3339,9 @@ export const fa: Dict = {
|
||||
'settings.skillsCreate': 'ایجاد',
|
||||
'settings.skillsSave': 'ذخیره',
|
||||
'settings.skillsSaving': 'در حال ذخیره…',
|
||||
'settings.skillsOverrideCreate': 'ایجاد بازنویسی کاربر',
|
||||
'settings.skillsOverrideSave': 'ذخیره بهعنوان بازنویسی کاربر',
|
||||
'settings.skillsBuiltInOverrideWarning': 'ویرایش این مهارت داخلی یک بازنویسی کاربر ایجاد میکند. مورد داخلی تا زمانی که بازنویسی را حذف کنید از فهرست پنهان میشود. ادامه میدهید؟',
|
||||
'settings.skillsFiles': 'فایلها',
|
||||
'settings.skillsNoFiles': 'هیچ فایلی در این پوشه مهارت نیست.',
|
||||
'settings.skillsNameRequired': 'نام مهارت الزامی است.',
|
||||
|
||||
@@ -3339,6 +3339,9 @@ export const fr: Dict = {
|
||||
'settings.skillsCreate': 'Créer',
|
||||
'settings.skillsSave': 'Enregistrer',
|
||||
'settings.skillsSaving': 'Enregistrement…',
|
||||
'settings.skillsOverrideCreate': 'Créer une surcharge utilisateur',
|
||||
'settings.skillsOverrideSave': 'Enregistrer comme surcharge utilisateur',
|
||||
'settings.skillsBuiltInOverrideWarning': 'Modifier cette compétence intégrée crée une surcharge utilisateur. L\'entrée intégrée sera masquée de la liste jusqu\'à ce que vous supprimiez la surcharge. Continuer ?',
|
||||
'settings.skillsFiles': 'Fichiers',
|
||||
'settings.skillsNoFiles': 'Aucun fichier dans ce dossier de Skill.',
|
||||
'settings.skillsNameRequired': 'Le nom du Skill est requis.',
|
||||
|
||||
@@ -3339,6 +3339,9 @@ export const hu: Dict = {
|
||||
'settings.skillsCreate': 'Létrehozás',
|
||||
'settings.skillsSave': 'Mentés',
|
||||
'settings.skillsSaving': 'Mentés…',
|
||||
'settings.skillsOverrideCreate': 'Felhasználói felülbírálás létrehozása',
|
||||
'settings.skillsOverrideSave': 'Mentés felhasználói felülbírálásként',
|
||||
'settings.skillsBuiltInOverrideWarning': 'A beépített készség szerkesztése felhasználói felülbírálást hoz létre. A beépített bejegyzés rejtve marad a listából, amíg nem törli a felülbírálást. Folytatja?',
|
||||
'settings.skillsFiles': 'Fájlok',
|
||||
'settings.skillsNoFiles': 'Nincs fájl ebben a készségmappában.',
|
||||
'settings.skillsNameRequired': 'A készség neve kötelező.',
|
||||
|
||||
@@ -3339,6 +3339,9 @@ export const id: Dict = {
|
||||
'settings.skillsCreate': 'Buat',
|
||||
'settings.skillsSave': 'Simpan',
|
||||
'settings.skillsSaving': 'Menyimpan…',
|
||||
'settings.skillsOverrideCreate': 'Buat penggantian pengguna',
|
||||
'settings.skillsOverrideSave': 'Simpan sebagai penggantian pengguna',
|
||||
'settings.skillsBuiltInOverrideWarning': 'Mengedit keterampilan bawaan ini akan membuat penggantian pengguna. Entri bawaan akan disembunyikan dari daftar hingga Anda menghapus penggantian. Lanjutkan?',
|
||||
'settings.skillsFiles': 'Berkas',
|
||||
'settings.skillsNoFiles': 'Tidak ada berkas dalam folder keterampilan ini.',
|
||||
'settings.skillsNameRequired': 'Nama keterampilan wajib diisi.',
|
||||
|
||||
@@ -3339,6 +3339,9 @@ export const it: Dict = {
|
||||
'settings.skillsCreate': 'Crea',
|
||||
'settings.skillsSave': 'Salva',
|
||||
'settings.skillsSaving': 'Salvataggio…',
|
||||
'settings.skillsOverrideCreate': 'Crea override utente',
|
||||
'settings.skillsOverrideSave': 'Salva come override utente',
|
||||
'settings.skillsBuiltInOverrideWarning': 'La modifica di questa competenza integrata crea un override utente. La voce integrata verrà nascosta dall\'elenco finché non elimini l\'override. Continuare?',
|
||||
'settings.skillsFiles': 'File',
|
||||
'settings.skillsNoFiles': 'Nessun file in questa cartella di competenza.',
|
||||
'settings.skillsNameRequired': 'Il nome dello skill è obbligatorio.',
|
||||
|
||||
@@ -3339,6 +3339,9 @@ export const ja: Dict = {
|
||||
'settings.skillsCreate': '作成',
|
||||
'settings.skillsSave': '保存',
|
||||
'settings.skillsSaving': '保存中…',
|
||||
'settings.skillsOverrideCreate': 'ユーザー上書きを作成',
|
||||
'settings.skillsOverrideSave': 'ユーザー上書きとして保存',
|
||||
'settings.skillsBuiltInOverrideWarning': 'この組み込みスキルを編集すると、ユーザー上書きが作成されます。上書きを削除するまで、組み込みの項目は一覧から非表示になります。続行しますか?',
|
||||
'settings.skillsFiles': 'ファイル',
|
||||
'settings.skillsNoFiles': 'このスキルフォルダーにファイルはありません。',
|
||||
'settings.skillsNameRequired': 'スキル名は必須です。',
|
||||
|
||||
@@ -3339,6 +3339,9 @@ export const ko: Dict = {
|
||||
'settings.skillsCreate': '만들기',
|
||||
'settings.skillsSave': '저장',
|
||||
'settings.skillsSaving': '저장 중…',
|
||||
'settings.skillsOverrideCreate': '사용자 재정의 만들기',
|
||||
'settings.skillsOverrideSave': '사용자 재정의로 저장',
|
||||
'settings.skillsBuiltInOverrideWarning': '이 기본 제공 스킬을 편집하면 사용자 재정의가 생성됩니다. 재정의를 삭제할 때까지 기본 제공 항목은 목록에서 숨겨집니다. 계속하시겠습니까?',
|
||||
'settings.skillsFiles': '파일',
|
||||
'settings.skillsNoFiles': '이 스킬 폴더에 파일이 없습니다.',
|
||||
'settings.skillsNameRequired': '스킬 이름은 필수입니다.',
|
||||
|
||||
@@ -3339,6 +3339,9 @@ export const pl: Dict = {
|
||||
'settings.skillsCreate': 'Utwórz',
|
||||
'settings.skillsSave': 'Zapisz',
|
||||
'settings.skillsSaving': 'Zapisywanie…',
|
||||
'settings.skillsOverrideCreate': 'Utwórz nadpisanie użytkownika',
|
||||
'settings.skillsOverrideSave': 'Zapisz jako nadpisanie użytkownika',
|
||||
'settings.skillsBuiltInOverrideWarning': 'Edycja tej wbudowanej umiejętności tworzy nadpisanie użytkownika. Wbudowany wpis zostanie ukryty z listy, dopóki nie usuniesz nadpisania. Kontynuować?',
|
||||
'settings.skillsFiles': 'Pliki',
|
||||
'settings.skillsNoFiles': 'Brak plików w tym folderze umiejętności.',
|
||||
'settings.skillsNameRequired': 'Nazwa umiejętności jest wymagana.',
|
||||
|
||||
@@ -3339,6 +3339,9 @@ export const ptBR: Dict = {
|
||||
'settings.skillsCreate': 'Criar',
|
||||
'settings.skillsSave': 'Salvar',
|
||||
'settings.skillsSaving': 'Salvando…',
|
||||
'settings.skillsOverrideCreate': 'Criar substituição do usuário',
|
||||
'settings.skillsOverrideSave': 'Salvar como substituição do usuário',
|
||||
'settings.skillsBuiltInOverrideWarning': 'Editar esta habilidade integrada cria uma substituição do usuário. A entrada integrada ficará oculta na lista até você excluir a substituição. Continuar?',
|
||||
'settings.skillsFiles': 'Arquivos',
|
||||
'settings.skillsNoFiles': 'Nenhum arquivo nesta pasta de habilidade.',
|
||||
'settings.skillsNameRequired': 'O nome da habilidade é obrigatório.',
|
||||
|
||||
@@ -3339,6 +3339,9 @@ export const ru: Dict = {
|
||||
'settings.skillsCreate': 'Создать',
|
||||
'settings.skillsSave': 'Сохранить',
|
||||
'settings.skillsSaving': 'Сохранение…',
|
||||
'settings.skillsOverrideCreate': 'Создать пользовательское переопределение',
|
||||
'settings.skillsOverrideSave': 'Сохранить как пользовательское переопределение',
|
||||
'settings.skillsBuiltInOverrideWarning': 'Редактирование этого встроенного навыка создаёт пользовательское переопределение. Встроенная запись будет скрыта из списка, пока вы не удалите переопределение. Продолжить?',
|
||||
'settings.skillsFiles': 'Файлы',
|
||||
'settings.skillsNoFiles': 'В папке этого навыка нет файлов.',
|
||||
'settings.skillsNameRequired': 'Имя навыка обязательно.',
|
||||
|
||||
@@ -3339,6 +3339,9 @@ export const th: Dict = {
|
||||
'settings.skillsCreate': 'สร้าง',
|
||||
'settings.skillsSave': 'บันทึก',
|
||||
'settings.skillsSaving': 'กำลังบันทึก…',
|
||||
'settings.skillsOverrideCreate': 'สร้างการแทนที่ของผู้ใช้',
|
||||
'settings.skillsOverrideSave': 'บันทึกเป็นการแทนที่ของผู้ใช้',
|
||||
'settings.skillsBuiltInOverrideWarning': 'การแก้ไขสกิลในตัวนี้จะสร้างการแทนที่ของผู้ใช้ รายการในตัวจะถูกซ่อนจากรายการจนกว่าคุณจะลบการแทนที่ ดำเนินการต่อหรือไม่?',
|
||||
'settings.skillsFiles': 'ไฟล์',
|
||||
'settings.skillsNoFiles': 'ไม่มีไฟล์ในโฟลเดอร์ทักษะนี้',
|
||||
'settings.skillsNameRequired': 'ต้องระบุชื่อทักษะ',
|
||||
|
||||
@@ -3339,6 +3339,9 @@ export const tr: Dict = {
|
||||
'settings.skillsCreate': 'Oluştur',
|
||||
'settings.skillsSave': 'Kaydet',
|
||||
'settings.skillsSaving': 'Kaydediliyor…',
|
||||
'settings.skillsOverrideCreate': 'Kullanıcı geçersiz kılması oluştur',
|
||||
'settings.skillsOverrideSave': 'Kullanıcı geçersiz kılması olarak kaydet',
|
||||
'settings.skillsBuiltInOverrideWarning': 'Bu yerleşik beceriyi düzenlemek bir kullanıcı geçersiz kılması oluşturur. Geçersiz kılmayı silene kadar yerleşik giriş listeden gizlenir. Devam edilsin mi?',
|
||||
'settings.skillsFiles': 'Dosyalar',
|
||||
'settings.skillsNoFiles': 'Bu yetenek klasöründe dosya yok.',
|
||||
'settings.skillsNameRequired': 'Beceri adı gereklidir.',
|
||||
|
||||
@@ -3339,6 +3339,9 @@ export const uk: Dict = {
|
||||
'settings.skillsCreate': 'Створити',
|
||||
'settings.skillsSave': 'Зберегти',
|
||||
'settings.skillsSaving': 'Збереження…',
|
||||
'settings.skillsOverrideCreate': 'Створити користувацьке перевизначення',
|
||||
'settings.skillsOverrideSave': 'Зберегти як користувацьке перевизначення',
|
||||
'settings.skillsBuiltInOverrideWarning': 'Редагування цього вбудованого вміння створює користувацьке перевизначення. Вбудований запис буде приховано зі списку, доки ви не видалите перевизначення. Продовжити?',
|
||||
'settings.skillsFiles': 'Файли',
|
||||
'settings.skillsNoFiles': 'У теці цієї навички немає файлів.',
|
||||
'settings.skillsNameRequired': 'Ім\'я навички обов\'язкове.',
|
||||
|
||||
@@ -3588,6 +3588,10 @@ export const zhCN: Dict = {
|
||||
"settings.skillsCreate": "创建",
|
||||
"settings.skillsSave": "保存",
|
||||
"settings.skillsSaving": "保存中…",
|
||||
"settings.skillsOverrideCreate": "创建用户覆盖",
|
||||
"settings.skillsOverrideSave": "另存为用户覆盖",
|
||||
"settings.skillsBuiltInOverrideWarning":
|
||||
"编辑此内置技能会创建一份用户覆盖。在你删除该覆盖之前,内置条目将从列表中隐藏。是否继续?",
|
||||
"settings.skillsFiles": "文件",
|
||||
"settings.skillsNoFiles": "该技能目录下暂无文件。",
|
||||
"settings.skillsNameRequired": "技能名称为必填项。",
|
||||
|
||||
@@ -3597,6 +3597,10 @@ export const zhTW: Dict = {
|
||||
"settings.skillsCreate": "建立",
|
||||
"settings.skillsSave": "儲存",
|
||||
"settings.skillsSaving": "儲存中…",
|
||||
"settings.skillsOverrideCreate": "建立使用者覆寫",
|
||||
"settings.skillsOverrideSave": "另存為使用者覆寫",
|
||||
"settings.skillsBuiltInOverrideWarning":
|
||||
"編輯此內建技能會建立一份使用者覆寫。在你刪除該覆寫之前,內建項目將從清單中隱藏。是否繼續?",
|
||||
"settings.skillsFiles": "檔案",
|
||||
"settings.skillsNoFiles": "此技能資料夾沒有檔案。",
|
||||
"settings.skillsNameRequired": "技能名稱為必填項。",
|
||||
|
||||
@@ -567,6 +567,9 @@ export interface Dict {
|
||||
'settings.skillsCreate': string;
|
||||
'settings.skillsSave': string;
|
||||
'settings.skillsSaving': string;
|
||||
'settings.skillsOverrideCreate': string;
|
||||
'settings.skillsOverrideSave': string;
|
||||
'settings.skillsBuiltInOverrideWarning': string;
|
||||
'settings.skillsFiles': string;
|
||||
'settings.skillsNoFiles': string;
|
||||
'settings.skillsNameRequired': string;
|
||||
|
||||
@@ -5,6 +5,8 @@ import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { SkillsSection } from '../../src/components/SkillsSection';
|
||||
import { I18nProvider } from '../../src/i18n';
|
||||
import { en } from '../../src/i18n/locales/en';
|
||||
import { zhCN } from '../../src/i18n/locales/zh-CN';
|
||||
import type { AppConfig } from '../../src/types';
|
||||
import type { SkillSummary } from '@open-design/contracts';
|
||||
|
||||
@@ -224,6 +226,92 @@ describe('SkillsSection', () => {
|
||||
expect(await within(row).findByTestId('skills-edit-form')).toBeTruthy();
|
||||
});
|
||||
|
||||
// Regression for the nettee / lefarcen follow-up on PR #4793: editing a
|
||||
// built-in skill must read as an explicit "user override" flow in every
|
||||
// locale, never a hardcoded-English or plain "Edit" / "Save" affordance.
|
||||
// Lock the override wording end to end — edit tooltip → confirmation →
|
||||
// form heading → submit — in the default English locale and one translated
|
||||
// zh-CN path, so a regression to the old wording (or to a missing locale
|
||||
// key) goes red.
|
||||
describe.each([
|
||||
{ locale: 'en' as const, dict: en },
|
||||
{ locale: 'zh-CN' as const, dict: zhCN },
|
||||
])('built-in skill override wording ($locale)', ({ locale, dict }) => {
|
||||
it('frames the edit affordance, confirmation, form, and submit as a user override', async () => {
|
||||
renderSkillsSection(
|
||||
[
|
||||
makeSkill({
|
||||
id: 'builtin-skill',
|
||||
name: 'Built-in skill',
|
||||
source: 'built-in',
|
||||
}),
|
||||
],
|
||||
{ locale },
|
||||
);
|
||||
|
||||
const row = await screen.findByTestId('skill-row-builtin-skill');
|
||||
|
||||
// The edit (pencil) tooltip reads "Create user override".
|
||||
expect(within(row).getByTestId('skills-edit').title).toBe(
|
||||
dict['settings.skillsOverrideCreate'],
|
||||
);
|
||||
|
||||
fireEvent.click(within(row).getByTestId('skills-edit'));
|
||||
|
||||
// The inline confirmation shows the localized override warning and an
|
||||
// override-framed confirm button — not the plain "Edit" label.
|
||||
expect(
|
||||
await within(row).findByText(dict['settings.skillsBuiltInOverrideWarning']),
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
within(row).getByTestId('skills-edit-builtin-confirm').textContent,
|
||||
).toBe(dict['settings.skillsOverrideCreate']);
|
||||
|
||||
fireEvent.click(within(row).getByTestId('skills-edit-builtin-confirm'));
|
||||
|
||||
// The override form heading and submit both read as "user override".
|
||||
const form = await within(row).findByTestId('skills-edit-form');
|
||||
expect(within(form).getByRole('heading', { level: 4 }).textContent).toBe(
|
||||
dict['settings.skillsOverrideCreate'],
|
||||
);
|
||||
expect(within(form).getByTestId('skills-save').textContent).toBe(
|
||||
dict['settings.skillsOverrideSave'],
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// The override wording must stay scoped to built-in skills: editing a
|
||||
// user-authored skill keeps the plain Edit / Save affordance and never
|
||||
// surfaces the override confirmation.
|
||||
it('keeps the plain edit/save wording for user skills', async () => {
|
||||
renderSkillsSection(
|
||||
[
|
||||
makeSkill({
|
||||
id: 'user-skill',
|
||||
name: 'User skill',
|
||||
source: 'user',
|
||||
}),
|
||||
],
|
||||
{ locale: 'en' },
|
||||
);
|
||||
|
||||
const row = await screen.findByTestId('skill-row-user-skill');
|
||||
expect(within(row).getByTestId('skills-edit').title).toBe(
|
||||
en['settings.skillsEdit'],
|
||||
);
|
||||
|
||||
fireEvent.click(within(row).getByTestId('skills-edit'));
|
||||
expect(within(row).queryByTestId('skills-edit-builtin-warning')).toBeNull();
|
||||
|
||||
const form = await within(row).findByTestId('skills-edit-form');
|
||||
expect(within(form).getByRole('heading', { level: 4 }).textContent).toBe(
|
||||
en['settings.skillsEdit'],
|
||||
);
|
||||
expect(within(form).getByTestId('skills-save').textContent).toBe(
|
||||
en['settings.skillsSave'],
|
||||
);
|
||||
});
|
||||
|
||||
it('matches localized built-in skill names and descriptions in search', async () => {
|
||||
renderSkillsSection(
|
||||
[
|
||||
|
||||
Reference in New Issue
Block a user