@jjlmoya/utils-developer 1.28.0 → 1.29.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/entries.ts +4 -1
- package/src/tests/entry_contract.test.ts +10 -0
- package/src/tests/i18n_coverage.test.ts +5 -2
- package/src/tests/locale_completeness.test.ts +1 -1
- package/src/tests/qa-test-helpers.ts +32 -0
- package/src/tests/qa_bibliography_links.test.ts +43 -0
- package/src/tests/qa_claim_evidence.test.ts +69 -0
- package/src/tests/qa_logic_reference_coverage.test.ts +46 -0
- package/src/tests/qa_runtime_i18n.test.ts +115 -0
- package/src/tests/tool_validation.test.ts +1 -1
- package/src/tool/aspectRatio/bibliography.astro +2 -10
- package/src/tool/aspectRatio/bibliography.ts +7 -0
- package/src/tool/aspectRatio/i18n/de.ts +2 -14
- package/src/tool/aspectRatio/i18n/en.ts +2 -14
- package/src/tool/aspectRatio/i18n/es.ts +2 -14
- package/src/tool/aspectRatio/i18n/fr.ts +2 -14
- package/src/tool/aspectRatio/i18n/id.ts +2 -14
- package/src/tool/aspectRatio/i18n/it.ts +2 -14
- package/src/tool/aspectRatio/i18n/ja.ts +2 -14
- package/src/tool/aspectRatio/i18n/ko.ts +2 -14
- package/src/tool/aspectRatio/i18n/nl.ts +2 -14
- package/src/tool/aspectRatio/i18n/pl.ts +2 -14
- package/src/tool/aspectRatio/i18n/pt.ts +2 -14
- package/src/tool/aspectRatio/i18n/ru.ts +2 -14
- package/src/tool/aspectRatio/i18n/sv.ts +2 -14
- package/src/tool/aspectRatio/i18n/tr.ts +2 -14
- package/src/tool/aspectRatio/i18n/zh.ts +2 -14
- package/src/tool/calculadoraTiempoDatos/bibliography.astro +2 -10
- package/src/tool/calculadoraTiempoDatos/bibliography.ts +8 -0
- package/src/tool/calculadoraTiempoDatos/component.astro +6 -2
- package/src/tool/calculadoraTiempoDatos/i18n/de.ts +2 -6
- package/src/tool/calculadoraTiempoDatos/i18n/en.ts +2 -6
- package/src/tool/calculadoraTiempoDatos/i18n/es.ts +2 -6
- package/src/tool/calculadoraTiempoDatos/i18n/fr.ts +2 -6
- package/src/tool/calculadoraTiempoDatos/i18n/id.ts +2 -6
- package/src/tool/calculadoraTiempoDatos/i18n/it.ts +2 -6
- package/src/tool/calculadoraTiempoDatos/i18n/ja.ts +2 -6
- package/src/tool/calculadoraTiempoDatos/i18n/ko.ts +2 -6
- package/src/tool/calculadoraTiempoDatos/i18n/nl.ts +2 -6
- package/src/tool/calculadoraTiempoDatos/i18n/pl.ts +2 -6
- package/src/tool/calculadoraTiempoDatos/i18n/pt.ts +2 -6
- package/src/tool/calculadoraTiempoDatos/i18n/ru.ts +2 -6
- package/src/tool/calculadoraTiempoDatos/i18n/sv.ts +2 -6
- package/src/tool/calculadoraTiempoDatos/i18n/tr.ts +2 -6
- package/src/tool/calculadoraTiempoDatos/i18n/zh.ts +2 -6
- package/src/tool/colorConverter/bibliography.astro +2 -10
- package/src/tool/colorConverter/bibliography.ts +7 -0
- package/src/tool/colorConverter/i18n/de.ts +2 -5
- package/src/tool/colorConverter/i18n/en.ts +5 -8
- package/src/tool/colorConverter/i18n/es.ts +4 -7
- package/src/tool/colorConverter/i18n/fr.ts +2 -5
- package/src/tool/colorConverter/i18n/id.ts +2 -5
- package/src/tool/colorConverter/i18n/it.ts +2 -5
- package/src/tool/colorConverter/i18n/ja.ts +2 -5
- package/src/tool/colorConverter/i18n/ko.ts +2 -5
- package/src/tool/colorConverter/i18n/nl.ts +2 -5
- package/src/tool/colorConverter/i18n/pl.ts +2 -5
- package/src/tool/colorConverter/i18n/pt.ts +2 -5
- package/src/tool/colorConverter/i18n/ru.ts +2 -5
- package/src/tool/colorConverter/i18n/sv.ts +2 -5
- package/src/tool/colorConverter/i18n/tr.ts +2 -5
- package/src/tool/colorConverter/i18n/zh.ts +2 -5
- package/src/tool/conversorExcelCsvHtml/bibliography.astro +2 -10
- package/src/tool/conversorExcelCsvHtml/bibliography.ts +8 -0
- package/src/tool/conversorExcelCsvHtml/component.astro +4 -3
- package/src/tool/conversorExcelCsvHtml/i18n/de.ts +2 -6
- package/src/tool/conversorExcelCsvHtml/i18n/en.ts +2 -6
- package/src/tool/conversorExcelCsvHtml/i18n/es.ts +2 -6
- package/src/tool/conversorExcelCsvHtml/i18n/fr.ts +2 -6
- package/src/tool/conversorExcelCsvHtml/i18n/id.ts +2 -6
- package/src/tool/conversorExcelCsvHtml/i18n/it.ts +2 -6
- package/src/tool/conversorExcelCsvHtml/i18n/ja.ts +2 -6
- package/src/tool/conversorExcelCsvHtml/i18n/ko.ts +2 -6
- package/src/tool/conversorExcelCsvHtml/i18n/nl.ts +2 -6
- package/src/tool/conversorExcelCsvHtml/i18n/pl.ts +2 -6
- package/src/tool/conversorExcelCsvHtml/i18n/pt.ts +2 -6
- package/src/tool/conversorExcelCsvHtml/i18n/ru.ts +2 -6
- package/src/tool/conversorExcelCsvHtml/i18n/sv.ts +2 -6
- package/src/tool/conversorExcelCsvHtml/i18n/tr.ts +2 -6
- package/src/tool/conversorExcelCsvHtml/i18n/zh.ts +2 -6
- package/src/tool/cronGenerator/bibliography.astro +2 -10
- package/src/tool/cronGenerator/bibliography.ts +6 -0
- package/src/tool/cronGenerator/component.astro +5 -1
- package/src/tool/cronGenerator/i18n/de.ts +2 -10
- package/src/tool/cronGenerator/i18n/en.ts +2 -10
- package/src/tool/cronGenerator/i18n/es.ts +2 -10
- package/src/tool/cronGenerator/i18n/fr.ts +2 -10
- package/src/tool/cronGenerator/i18n/id.ts +2 -10
- package/src/tool/cronGenerator/i18n/it.ts +2 -10
- package/src/tool/cronGenerator/i18n/ja.ts +2 -10
- package/src/tool/cronGenerator/i18n/ko.ts +2 -10
- package/src/tool/cronGenerator/i18n/nl.ts +2 -10
- package/src/tool/cronGenerator/i18n/pl.ts +2 -10
- package/src/tool/cronGenerator/i18n/pt.ts +2 -10
- package/src/tool/cronGenerator/i18n/ru.ts +2 -10
- package/src/tool/cronGenerator/i18n/sv.ts +2 -10
- package/src/tool/cronGenerator/i18n/tr.ts +2 -10
- package/src/tool/cronGenerator/i18n/zh.ts +2 -10
- package/src/tool/cssBoxShadowGenerator/component.astro +2 -1
- package/src/tool/cssBoxShadowGenerator/logic.test.ts +28 -0
- package/src/tool/cssSpecificityCalculator/bibliography.astro +2 -10
- package/src/tool/cssSpecificityCalculator/bibliography.ts +6 -0
- package/src/tool/cssSpecificityCalculator/i18n/de.ts +2 -10
- package/src/tool/cssSpecificityCalculator/i18n/en.ts +2 -10
- package/src/tool/cssSpecificityCalculator/i18n/es.ts +2 -10
- package/src/tool/cssSpecificityCalculator/i18n/fr.ts +2 -10
- package/src/tool/cssSpecificityCalculator/i18n/id.ts +2 -10
- package/src/tool/cssSpecificityCalculator/i18n/it.ts +2 -10
- package/src/tool/cssSpecificityCalculator/i18n/ja.ts +2 -10
- package/src/tool/cssSpecificityCalculator/i18n/ko.ts +2 -10
- package/src/tool/cssSpecificityCalculator/i18n/nl.ts +2 -10
- package/src/tool/cssSpecificityCalculator/i18n/pl.ts +2 -10
- package/src/tool/cssSpecificityCalculator/i18n/pt.ts +2 -10
- package/src/tool/cssSpecificityCalculator/i18n/ru.ts +2 -10
- package/src/tool/cssSpecificityCalculator/i18n/sv.ts +2 -10
- package/src/tool/cssSpecificityCalculator/i18n/tr.ts +2 -10
- package/src/tool/cssSpecificityCalculator/i18n/zh.ts +2 -10
- package/src/tool/cssToInlineConverter/bibliography.astro +2 -10
- package/src/tool/cssToInlineConverter/bibliography.ts +7 -0
- package/src/tool/cssToInlineConverter/i18n/de.ts +2 -14
- package/src/tool/cssToInlineConverter/i18n/en.ts +4 -16
- package/src/tool/cssToInlineConverter/i18n/es.ts +5 -17
- package/src/tool/cssToInlineConverter/i18n/fr.ts +2 -14
- package/src/tool/cssToInlineConverter/i18n/id.ts +2 -14
- package/src/tool/cssToInlineConverter/i18n/it.ts +2 -14
- package/src/tool/cssToInlineConverter/i18n/ja.ts +2 -14
- package/src/tool/cssToInlineConverter/i18n/ko.ts +2 -14
- package/src/tool/cssToInlineConverter/i18n/nl.ts +2 -14
- package/src/tool/cssToInlineConverter/i18n/pl.ts +2 -14
- package/src/tool/cssToInlineConverter/i18n/pt.ts +2 -14
- package/src/tool/cssToInlineConverter/i18n/ru.ts +2 -14
- package/src/tool/cssToInlineConverter/i18n/sv.ts +2 -14
- package/src/tool/cssToInlineConverter/i18n/tr.ts +2 -14
- package/src/tool/cssToInlineConverter/i18n/zh.ts +2 -14
- package/src/tool/duplicateCssRemover/bibliography.astro +2 -10
- package/src/tool/duplicateCssRemover/bibliography.ts +7 -0
- package/src/tool/duplicateCssRemover/i18n/de.ts +2 -14
- package/src/tool/duplicateCssRemover/i18n/en.ts +4 -16
- package/src/tool/duplicateCssRemover/i18n/es.ts +4 -16
- package/src/tool/duplicateCssRemover/i18n/fr.ts +2 -14
- package/src/tool/duplicateCssRemover/i18n/id.ts +2 -14
- package/src/tool/duplicateCssRemover/i18n/it.ts +2 -14
- package/src/tool/duplicateCssRemover/i18n/ja.ts +2 -14
- package/src/tool/duplicateCssRemover/i18n/ko.ts +2 -14
- package/src/tool/duplicateCssRemover/i18n/nl.ts +2 -14
- package/src/tool/duplicateCssRemover/i18n/pl.ts +2 -14
- package/src/tool/duplicateCssRemover/i18n/pt.ts +2 -14
- package/src/tool/duplicateCssRemover/i18n/ru.ts +2 -14
- package/src/tool/duplicateCssRemover/i18n/sv.ts +2 -14
- package/src/tool/duplicateCssRemover/i18n/tr.ts +2 -14
- package/src/tool/duplicateCssRemover/i18n/zh.ts +2 -14
- package/src/tool/generadorSecurityTxt/bibliography.astro +2 -10
- package/src/tool/generadorSecurityTxt/bibliography.ts +7 -0
- package/src/tool/generadorSecurityTxt/component.astro +1 -2
- package/src/tool/generadorSecurityTxt/i18n/de.ts +2 -5
- package/src/tool/generadorSecurityTxt/i18n/en.ts +2 -5
- package/src/tool/generadorSecurityTxt/i18n/es.ts +2 -5
- package/src/tool/generadorSecurityTxt/i18n/fr.ts +2 -5
- package/src/tool/generadorSecurityTxt/i18n/id.ts +2 -5
- package/src/tool/generadorSecurityTxt/i18n/it.ts +2 -5
- package/src/tool/generadorSecurityTxt/i18n/ja.ts +2 -5
- package/src/tool/generadorSecurityTxt/i18n/ko.ts +2 -5
- package/src/tool/generadorSecurityTxt/i18n/nl.ts +2 -5
- package/src/tool/generadorSecurityTxt/i18n/pl.ts +2 -5
- package/src/tool/generadorSecurityTxt/i18n/pt.ts +2 -5
- package/src/tool/generadorSecurityTxt/i18n/ru.ts +2 -5
- package/src/tool/generadorSecurityTxt/i18n/sv.ts +2 -5
- package/src/tool/generadorSecurityTxt/i18n/tr.ts +2 -5
- package/src/tool/generadorSecurityTxt/i18n/zh.ts +2 -5
- package/src/tool/hashGenerator/bibliography.astro +2 -10
- package/src/tool/hashGenerator/bibliography.ts +7 -0
- package/src/tool/hashGenerator/i18n/de.ts +2 -5
- package/src/tool/hashGenerator/i18n/en.ts +2 -5
- package/src/tool/hashGenerator/i18n/es.ts +2 -5
- package/src/tool/hashGenerator/i18n/fr.ts +2 -5
- package/src/tool/hashGenerator/i18n/id.ts +2 -5
- package/src/tool/hashGenerator/i18n/it.ts +2 -5
- package/src/tool/hashGenerator/i18n/ja.ts +2 -5
- package/src/tool/hashGenerator/i18n/ko.ts +2 -5
- package/src/tool/hashGenerator/i18n/nl.ts +2 -5
- package/src/tool/hashGenerator/i18n/pl.ts +2 -5
- package/src/tool/hashGenerator/i18n/pt.ts +2 -5
- package/src/tool/hashGenerator/i18n/ru.ts +2 -5
- package/src/tool/hashGenerator/i18n/sv.ts +2 -5
- package/src/tool/hashGenerator/i18n/tr.ts +2 -5
- package/src/tool/hashGenerator/i18n/zh.ts +2 -5
- package/src/tool/inspectorCertificadosSsl/bibliography.astro +2 -10
- package/src/tool/inspectorCertificadosSsl/bibliography.ts +8 -0
- package/src/tool/inspectorCertificadosSsl/component.astro +4 -3
- package/src/tool/inspectorCertificadosSsl/i18n/de.ts +2 -6
- package/src/tool/inspectorCertificadosSsl/i18n/en.ts +2 -6
- package/src/tool/inspectorCertificadosSsl/i18n/es.ts +2 -6
- package/src/tool/inspectorCertificadosSsl/i18n/fr.ts +2 -6
- package/src/tool/inspectorCertificadosSsl/i18n/id.ts +2 -6
- package/src/tool/inspectorCertificadosSsl/i18n/it.ts +2 -6
- package/src/tool/inspectorCertificadosSsl/i18n/ja.ts +2 -6
- package/src/tool/inspectorCertificadosSsl/i18n/ko.ts +2 -6
- package/src/tool/inspectorCertificadosSsl/i18n/nl.ts +2 -6
- package/src/tool/inspectorCertificadosSsl/i18n/pl.ts +2 -6
- package/src/tool/inspectorCertificadosSsl/i18n/pt.ts +2 -6
- package/src/tool/inspectorCertificadosSsl/i18n/ru.ts +2 -6
- package/src/tool/inspectorCertificadosSsl/i18n/sv.ts +2 -6
- package/src/tool/inspectorCertificadosSsl/i18n/tr.ts +2 -6
- package/src/tool/inspectorCertificadosSsl/i18n/zh.ts +2 -6
- package/src/tool/jsonFormatter/bibliography.astro +2 -10
- package/src/tool/jsonFormatter/bibliography.ts +8 -0
- package/src/tool/jsonFormatter/i18n/de.ts +2 -18
- package/src/tool/jsonFormatter/i18n/en.ts +2 -18
- package/src/tool/jsonFormatter/i18n/es.ts +2 -18
- package/src/tool/jsonFormatter/i18n/fr.ts +2 -18
- package/src/tool/jsonFormatter/i18n/id.ts +2 -18
- package/src/tool/jsonFormatter/i18n/it.ts +2 -18
- package/src/tool/jsonFormatter/i18n/ja.ts +2 -18
- package/src/tool/jsonFormatter/i18n/ko.ts +2 -18
- package/src/tool/jsonFormatter/i18n/nl.ts +2 -18
- package/src/tool/jsonFormatter/i18n/pl.ts +2 -18
- package/src/tool/jsonFormatter/i18n/pt.ts +2 -18
- package/src/tool/jsonFormatter/i18n/ru.ts +2 -18
- package/src/tool/jsonFormatter/i18n/sv.ts +2 -18
- package/src/tool/jsonFormatter/i18n/tr.ts +2 -18
- package/src/tool/jsonFormatter/i18n/zh.ts +2 -18
- package/src/tool/jwtDecoder/component.astro +2 -1
- package/src/tool/jwtDecoder/logic.test.ts +32 -0
- package/src/tool/keycode/bibliography.astro +2 -10
- package/src/tool/keycode/bibliography.ts +7 -0
- package/src/tool/keycode/i18n/de.ts +2 -14
- package/src/tool/keycode/i18n/en.ts +2 -14
- package/src/tool/keycode/i18n/es.ts +2 -14
- package/src/tool/keycode/i18n/fr.ts +2 -14
- package/src/tool/keycode/i18n/id.ts +2 -14
- package/src/tool/keycode/i18n/it.ts +2 -14
- package/src/tool/keycode/i18n/ja.ts +2 -14
- package/src/tool/keycode/i18n/ko.ts +2 -14
- package/src/tool/keycode/i18n/nl.ts +2 -14
- package/src/tool/keycode/i18n/pl.ts +2 -14
- package/src/tool/keycode/i18n/pt.ts +2 -14
- package/src/tool/keycode/i18n/ru.ts +2 -14
- package/src/tool/keycode/i18n/sv.ts +2 -14
- package/src/tool/keycode/i18n/tr.ts +2 -14
- package/src/tool/keycode/i18n/zh.ts +2 -14
- package/src/tool/llmCostCalculator/bibliography.astro +2 -10
- package/src/tool/llmCostCalculator/bibliography.ts +8 -0
- package/src/tool/llmCostCalculator/i18n/de.ts +2 -18
- package/src/tool/llmCostCalculator/i18n/en.ts +2 -18
- package/src/tool/llmCostCalculator/i18n/es.ts +2 -18
- package/src/tool/llmCostCalculator/i18n/fr.ts +2 -18
- package/src/tool/llmCostCalculator/i18n/id.ts +2 -18
- package/src/tool/llmCostCalculator/i18n/it.ts +2 -18
- package/src/tool/llmCostCalculator/i18n/ja.ts +2 -18
- package/src/tool/llmCostCalculator/i18n/ko.ts +2 -18
- package/src/tool/llmCostCalculator/i18n/nl.ts +2 -18
- package/src/tool/llmCostCalculator/i18n/pl.ts +2 -18
- package/src/tool/llmCostCalculator/i18n/pt.ts +2 -18
- package/src/tool/llmCostCalculator/i18n/ru.ts +2 -18
- package/src/tool/llmCostCalculator/i18n/sv.ts +2 -18
- package/src/tool/llmCostCalculator/i18n/tr.ts +2 -18
- package/src/tool/llmCostCalculator/i18n/zh.ts +2 -18
- package/src/tool/mobileMockupGenerator/bibliography.astro +2 -10
- package/src/tool/mobileMockupGenerator/bibliography.ts +6 -0
- package/src/tool/mobileMockupGenerator/i18n/de.ts +2 -4
- package/src/tool/mobileMockupGenerator/i18n/en.ts +2 -4
- package/src/tool/mobileMockupGenerator/i18n/es.ts +2 -4
- package/src/tool/mobileMockupGenerator/i18n/fr.ts +2 -4
- package/src/tool/mobileMockupGenerator/i18n/id.ts +2 -4
- package/src/tool/mobileMockupGenerator/i18n/it.ts +2 -4
- package/src/tool/mobileMockupGenerator/i18n/ja.ts +2 -4
- package/src/tool/mobileMockupGenerator/i18n/ko.ts +2 -4
- package/src/tool/mobileMockupGenerator/i18n/nl.ts +2 -4
- package/src/tool/mobileMockupGenerator/i18n/pl.ts +2 -1
- package/src/tool/mobileMockupGenerator/i18n/pt.ts +2 -4
- package/src/tool/mobileMockupGenerator/i18n/ru.ts +2 -4
- package/src/tool/mobileMockupGenerator/i18n/sv.ts +2 -4
- package/src/tool/mobileMockupGenerator/i18n/tr.ts +2 -4
- package/src/tool/mobileMockupGenerator/i18n/zh.ts +2 -4
- package/src/tool/musicalTypography/bibliography.astro +2 -10
- package/src/tool/musicalTypography/bibliography.ts +7 -0
- package/src/tool/musicalTypography/i18n/de.ts +2 -14
- package/src/tool/musicalTypography/i18n/en.ts +3 -15
- package/src/tool/musicalTypography/i18n/es.ts +3 -15
- package/src/tool/musicalTypography/i18n/fr.ts +2 -14
- package/src/tool/musicalTypography/i18n/id.ts +2 -14
- package/src/tool/musicalTypography/i18n/it.ts +2 -14
- package/src/tool/musicalTypography/i18n/ja.ts +2 -14
- package/src/tool/musicalTypography/i18n/ko.ts +2 -14
- package/src/tool/musicalTypography/i18n/nl.ts +2 -14
- package/src/tool/musicalTypography/i18n/pl.ts +2 -14
- package/src/tool/musicalTypography/i18n/pt.ts +2 -5
- package/src/tool/musicalTypography/i18n/ru.ts +2 -5
- package/src/tool/musicalTypography/i18n/sv.ts +2 -1
- package/src/tool/musicalTypography/i18n/tr.ts +2 -1
- package/src/tool/musicalTypography/i18n/zh.ts +2 -1
- package/src/tool/placeholderGenerator/bibliography.astro +2 -10
- package/src/tool/placeholderGenerator/bibliography.ts +7 -0
- package/src/tool/placeholderGenerator/i18n/de.ts +2 -14
- package/src/tool/placeholderGenerator/i18n/en.ts +4 -16
- package/src/tool/placeholderGenerator/i18n/es.ts +4 -16
- package/src/tool/placeholderGenerator/i18n/fr.ts +2 -14
- package/src/tool/placeholderGenerator/i18n/id.ts +2 -14
- package/src/tool/placeholderGenerator/i18n/it.ts +2 -14
- package/src/tool/placeholderGenerator/i18n/ja.ts +2 -14
- package/src/tool/placeholderGenerator/i18n/ko.ts +2 -14
- package/src/tool/placeholderGenerator/i18n/nl.ts +2 -14
- package/src/tool/placeholderGenerator/i18n/pl.ts +2 -14
- package/src/tool/placeholderGenerator/i18n/pt.ts +2 -14
- package/src/tool/placeholderGenerator/i18n/ru.ts +2 -14
- package/src/tool/placeholderGenerator/i18n/sv.ts +2 -14
- package/src/tool/placeholderGenerator/i18n/tr.ts +2 -14
- package/src/tool/placeholderGenerator/i18n/zh.ts +2 -14
- package/src/tool/promoteThisWebsite/asset-bindings.ts +73 -0
- package/src/tool/promoteThisWebsite/bibliography.astro +14 -0
- package/src/tool/promoteThisWebsite/bibliography.ts +14 -0
- package/src/tool/promoteThisWebsite/component.astro +136 -0
- package/src/tool/promoteThisWebsite/control-views.ts +83 -0
- package/src/tool/promoteThisWebsite/controller.ts +232 -0
- package/src/tool/promoteThisWebsite/dom-views.ts +322 -0
- package/src/tool/promoteThisWebsite/entry.ts +16 -0
- package/src/tool/promoteThisWebsite/evaluator.ts +9 -0
- package/src/tool/promoteThisWebsite/i18n/en.ts +111 -0
- package/src/tool/promoteThisWebsite/i18n/es.ts +111 -0
- package/src/tool/promoteThisWebsite/index.ts +11 -0
- package/src/tool/promoteThisWebsite/jjlmoya-overlay.png +0 -0
- package/src/tool/promoteThisWebsite/logic.test.ts +29 -0
- package/src/tool/promoteThisWebsite/logic.ts +108 -0
- package/src/tool/promoteThisWebsite/pixel-cat.png +0 -0
- package/src/tool/promoteThisWebsite/promote-this-website.css +322 -0
- package/src/tool/promoteThisWebsite/seo.astro +15 -0
- package/src/tool/promoteThisWebsite/storage.ts +28 -0
- package/src/tool/promoteThisWebsite/title-view.ts +206 -0
- package/src/tool/promoteThisWebsite/ui.ts +59 -0
- package/src/tool/promptLibrary/bibliography.astro +2 -10
- package/src/tool/promptLibrary/bibliography.ts +6 -0
- package/src/tool/promptLibrary/i18n/de.ts +2 -4
- package/src/tool/promptLibrary/i18n/en.ts +2 -4
- package/src/tool/promptLibrary/i18n/es.ts +2 -4
- package/src/tool/promptLibrary/i18n/fr.ts +2 -4
- package/src/tool/promptLibrary/i18n/id.ts +2 -4
- package/src/tool/promptLibrary/i18n/it.ts +2 -4
- package/src/tool/promptLibrary/i18n/ja.ts +2 -4
- package/src/tool/promptLibrary/i18n/ko.ts +2 -4
- package/src/tool/promptLibrary/i18n/nl.ts +2 -4
- package/src/tool/promptLibrary/i18n/pl.ts +2 -1
- package/src/tool/promptLibrary/i18n/pt.ts +2 -4
- package/src/tool/promptLibrary/i18n/ru.ts +2 -4
- package/src/tool/promptLibrary/i18n/sv.ts +2 -4
- package/src/tool/promptLibrary/i18n/tr.ts +2 -4
- package/src/tool/promptLibrary/i18n/zh.ts +2 -4
- package/src/tool/readabilityCalculator/bibliography.astro +2 -10
- package/src/tool/readabilityCalculator/bibliography.ts +7 -0
- package/src/tool/readabilityCalculator/i18n/de.ts +2 -5
- package/src/tool/readabilityCalculator/i18n/en.ts +2 -5
- package/src/tool/readabilityCalculator/i18n/es.ts +2 -5
- package/src/tool/readabilityCalculator/i18n/fr.ts +2 -5
- package/src/tool/readabilityCalculator/i18n/id.ts +2 -5
- package/src/tool/readabilityCalculator/i18n/it.ts +2 -5
- package/src/tool/readabilityCalculator/i18n/ja.ts +2 -5
- package/src/tool/readabilityCalculator/i18n/ko.ts +2 -5
- package/src/tool/readabilityCalculator/i18n/nl.ts +2 -5
- package/src/tool/readabilityCalculator/i18n/pl.ts +2 -1
- package/src/tool/readabilityCalculator/i18n/pt.ts +2 -5
- package/src/tool/readabilityCalculator/i18n/ru.ts +2 -5
- package/src/tool/readabilityCalculator/i18n/sv.ts +2 -5
- package/src/tool/readabilityCalculator/i18n/tr.ts +2 -5
- package/src/tool/readabilityCalculator/i18n/zh.ts +2 -5
- package/src/tool/serpPixelSimulator/controller.ts +1 -0
- package/src/tool/serpPixelSimulator/i18n/en.ts +2 -2
- package/src/tool/serpPixelSimulator/i18n/es.ts +2 -2
- package/src/tool/serpPixelSimulator/logic.test.ts +25 -0
- package/src/tool/svgSanitizer/bibliography.astro +2 -10
- package/src/tool/svgSanitizer/bibliography.ts +7 -0
- package/src/tool/svgSanitizer/i18n/de.ts +2 -5
- package/src/tool/svgSanitizer/i18n/en.ts +2 -5
- package/src/tool/svgSanitizer/i18n/es.ts +2 -5
- package/src/tool/svgSanitizer/i18n/fr.ts +2 -5
- package/src/tool/svgSanitizer/i18n/id.ts +2 -5
- package/src/tool/svgSanitizer/i18n/it.ts +2 -5
- package/src/tool/svgSanitizer/i18n/ja.ts +2 -5
- package/src/tool/svgSanitizer/i18n/ko.ts +2 -5
- package/src/tool/svgSanitizer/i18n/nl.ts +2 -5
- package/src/tool/svgSanitizer/i18n/pl.ts +2 -5
- package/src/tool/svgSanitizer/i18n/pt.ts +2 -1
- package/src/tool/svgSanitizer/i18n/ru.ts +2 -5
- package/src/tool/svgSanitizer/i18n/sv.ts +2 -5
- package/src/tool/svgSanitizer/i18n/tr.ts +2 -5
- package/src/tool/svgSanitizer/i18n/zh.ts +2 -5
- package/src/tool/svgToCss/bibliography.astro +2 -10
- package/src/tool/svgToCss/bibliography.ts +8 -0
- package/src/tool/svgToCss/i18n/de.ts +2 -18
- package/src/tool/svgToCss/i18n/en.ts +2 -18
- package/src/tool/svgToCss/i18n/es.ts +2 -18
- package/src/tool/svgToCss/i18n/fr.ts +2 -18
- package/src/tool/svgToCss/i18n/id.ts +2 -18
- package/src/tool/svgToCss/i18n/it.ts +2 -18
- package/src/tool/svgToCss/i18n/ja.ts +2 -18
- package/src/tool/svgToCss/i18n/ko.ts +2 -18
- package/src/tool/svgToCss/i18n/nl.ts +2 -18
- package/src/tool/svgToCss/i18n/pl.ts +2 -18
- package/src/tool/svgToCss/i18n/pt.ts +2 -18
- package/src/tool/svgToCss/i18n/ru.ts +2 -18
- package/src/tool/svgToCss/i18n/sv.ts +2 -18
- package/src/tool/svgToCss/i18n/tr.ts +2 -18
- package/src/tool/svgToCss/i18n/zh.ts +2 -18
- package/src/tool/urlCleaner/bibliography.astro +2 -10
- package/src/tool/urlCleaner/bibliography.ts +7 -0
- package/src/tool/urlCleaner/i18n/de.ts +2 -5
- package/src/tool/urlCleaner/i18n/en.ts +2 -5
- package/src/tool/urlCleaner/i18n/es.ts +2 -5
- package/src/tool/urlCleaner/i18n/fr.ts +2 -5
- package/src/tool/urlCleaner/i18n/id.ts +2 -5
- package/src/tool/urlCleaner/i18n/it.ts +2 -5
- package/src/tool/urlCleaner/i18n/ja.ts +2 -5
- package/src/tool/urlCleaner/i18n/ko.ts +2 -5
- package/src/tool/urlCleaner/i18n/nl.ts +2 -5
- package/src/tool/urlCleaner/i18n/pl.ts +2 -1
- package/src/tool/urlCleaner/i18n/pt.ts +2 -5
- package/src/tool/urlCleaner/i18n/ru.ts +2 -5
- package/src/tool/urlCleaner/i18n/sv.ts +2 -5
- package/src/tool/urlCleaner/i18n/tr.ts +2 -5
- package/src/tool/urlCleaner/i18n/zh.ts +2 -5
- package/src/tool/urlEncoderDecoder/bibliography.astro +2 -10
- package/src/tool/urlEncoderDecoder/bibliography.ts +7 -0
- package/src/tool/urlEncoderDecoder/i18n/de.ts +2 -14
- package/src/tool/urlEncoderDecoder/i18n/en.ts +3 -15
- package/src/tool/urlEncoderDecoder/i18n/es.ts +3 -15
- package/src/tool/urlEncoderDecoder/i18n/fr.ts +2 -14
- package/src/tool/urlEncoderDecoder/i18n/id.ts +2 -14
- package/src/tool/urlEncoderDecoder/i18n/it.ts +2 -14
- package/src/tool/urlEncoderDecoder/i18n/ja.ts +2 -14
- package/src/tool/urlEncoderDecoder/i18n/ko.ts +2 -14
- package/src/tool/urlEncoderDecoder/i18n/nl.ts +2 -14
- package/src/tool/urlEncoderDecoder/i18n/pl.ts +2 -14
- package/src/tool/urlEncoderDecoder/i18n/pt.ts +2 -14
- package/src/tool/urlEncoderDecoder/i18n/ru.ts +2 -14
- package/src/tool/urlEncoderDecoder/i18n/sv.ts +2 -14
- package/src/tool/urlEncoderDecoder/i18n/tr.ts +2 -14
- package/src/tool/urlEncoderDecoder/i18n/zh.ts +2 -14
- package/src/tool/utmGenerator/bibliography.astro +2 -10
- package/src/tool/utmGenerator/bibliography.ts +6 -0
- package/src/tool/utmGenerator/i18n/de.ts +2 -4
- package/src/tool/utmGenerator/i18n/en.ts +2 -4
- package/src/tool/utmGenerator/i18n/es.ts +2 -4
- package/src/tool/utmGenerator/i18n/fr.ts +2 -4
- package/src/tool/utmGenerator/i18n/id.ts +2 -4
- package/src/tool/utmGenerator/i18n/it.ts +2 -4
- package/src/tool/utmGenerator/i18n/ja.ts +2 -4
- package/src/tool/utmGenerator/i18n/ko.ts +2 -4
- package/src/tool/utmGenerator/i18n/nl.ts +2 -4
- package/src/tool/utmGenerator/i18n/pl.ts +2 -4
- package/src/tool/utmGenerator/i18n/pt.ts +2 -4
- package/src/tool/utmGenerator/i18n/ru.ts +2 -4
- package/src/tool/utmGenerator/i18n/sv.ts +2 -4
- package/src/tool/utmGenerator/i18n/tr.ts +2 -4
- package/src/tool/utmGenerator/i18n/zh.ts +2 -4
- package/src/tool/visualCssGridFlexboxGenerator/component.astro +1 -0
- package/src/tool/visualCssGridFlexboxGenerator/logic.test.ts +29 -0
- package/src/tool/visualCssGridFlexboxGenerator/workspace.ts +13 -1
- package/src/tools.ts +2 -1
package/package.json
CHANGED
package/src/entries.ts
CHANGED
|
@@ -54,6 +54,8 @@ export { visualCssGridFlexboxGenerator } from './tool/visualCssGridFlexboxGenera
|
|
|
54
54
|
export type { VisualCssGridFlexboxGeneratorLocaleContent } from './tool/visualCssGridFlexboxGenerator/entry';
|
|
55
55
|
export { cssBoxShadowGenerator } from './tool/cssBoxShadowGenerator/entry';
|
|
56
56
|
export type { CssBoxShadowGeneratorLocaleContent } from './tool/cssBoxShadowGenerator/entry';
|
|
57
|
+
export { promoteThisWebsite } from './tool/promoteThisWebsite/entry';
|
|
58
|
+
export type { PromoteThisWebsiteLocaleContent } from './tool/promoteThisWebsite/entry';
|
|
57
59
|
export { developerCategory } from './category';
|
|
58
60
|
import { aspectRatio } from './tool/aspectRatio/entry';
|
|
59
61
|
import { calculadoraTiempoDatos } from './tool/calculadoraTiempoDatos/entry';
|
|
@@ -83,4 +85,5 @@ import { serpPixelSimulator } from './tool/serpPixelSimulator/entry';
|
|
|
83
85
|
import { jwtDecoder } from './tool/jwtDecoder/entry';
|
|
84
86
|
import { visualCssGridFlexboxGenerator } from './tool/visualCssGridFlexboxGenerator/entry';
|
|
85
87
|
import { cssBoxShadowGenerator } from './tool/cssBoxShadowGenerator/entry';
|
|
86
|
-
|
|
88
|
+
import { promoteThisWebsite } from './tool/promoteThisWebsite/entry';
|
|
89
|
+
export const ALL_ENTRIES = [aspectRatio, calculadoraTiempoDatos, colorConverter, conversorExcelCsvHtml, cronGenerator, cssSpecificityCalculator, cssToInlineConverter, duplicateCssRemover, generadorSecurityTxt, hashGenerator, inspectorCertificadosSsl, jsonFormatter, keycode, llmCostCalculator, mobileMockupGenerator, musicalTypography, placeholderGenerator, promptLibrary, readabilityCalculator, svgSanitizer, svgToCss, urlCleaner, urlEncoderDecoder, utmGenerator, serpPixelSimulator, jwtDecoder, visualCssGridFlexboxGenerator, cssBoxShadowGenerator, promoteThisWebsite];
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { ALL_ENTRIES } from '../entries';
|
|
3
|
+
|
|
4
|
+
describe('Tool entry consumer contract', () => {
|
|
5
|
+
it('every published tool exposes a Spanish locale loader', () => {
|
|
6
|
+
for (const entry of ALL_ENTRIES) {
|
|
7
|
+
expect(typeof entry.i18n.es, `${entry.id} must expose i18n.es`).toBe('function');
|
|
8
|
+
}
|
|
9
|
+
});
|
|
10
|
+
});
|
|
@@ -4,6 +4,7 @@ import { ALL_TOOLS } from '../tools';
|
|
|
4
4
|
const EXPECTED_LOCALES = [
|
|
5
5
|
'de', 'en', 'es', 'fr', 'id', 'it', 'ja', 'ko', 'nl', 'pl', 'pt', 'ru', 'sv', 'tr', 'zh'
|
|
6
6
|
];
|
|
7
|
+
const ENGLISH_FIRST_TOOLS = new Set(['promote-this-website']);
|
|
7
8
|
|
|
8
9
|
describe('I18n Coverage Validation', () => {
|
|
9
10
|
it('all tools should be registered', () => {
|
|
@@ -14,7 +15,8 @@ describe('I18n Coverage Validation', () => {
|
|
|
14
15
|
describe(`Tool: ${entry.id}`, () => {
|
|
15
16
|
it('should have all 15 required locales', () => {
|
|
16
17
|
const registeredLocales = Object.keys(entry.i18n);
|
|
17
|
-
|
|
18
|
+
const requiredLocales = ENGLISH_FIRST_TOOLS.has(entry.id) ? ['en'] : EXPECTED_LOCALES;
|
|
19
|
+
requiredLocales.forEach((locale) => {
|
|
18
20
|
expect(
|
|
19
21
|
registeredLocales,
|
|
20
22
|
`Tool "${entry.id}" is missing locale "${locale}"`,
|
|
@@ -23,7 +25,8 @@ describe('I18n Coverage Validation', () => {
|
|
|
23
25
|
});
|
|
24
26
|
|
|
25
27
|
it('all locale loaders should be functions', () => {
|
|
26
|
-
|
|
28
|
+
const requiredLocales = ENGLISH_FIRST_TOOLS.has(entry.id) ? ['en'] : EXPECTED_LOCALES;
|
|
29
|
+
requiredLocales.forEach((locale) => {
|
|
27
30
|
const loader = entry.i18n[locale as keyof typeof entry.i18n];
|
|
28
31
|
expect(
|
|
29
32
|
typeof loader,
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { readdirSync, readFileSync, statSync } from 'node:fs';
|
|
2
|
+
import { join, relative } from 'node:path';
|
|
3
|
+
|
|
4
|
+
export const repositoryRoot = process.cwd();
|
|
5
|
+
export const toolRoot = join(repositoryRoot, 'src', 'tool');
|
|
6
|
+
|
|
7
|
+
export function listToolDirectories(): string[] {
|
|
8
|
+
return readdirSync(toolRoot)
|
|
9
|
+
.map((name) => join(toolRoot, name))
|
|
10
|
+
.filter((path) => statSync(path).isDirectory());
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function findFiles(directory: string, extensions: string[]): string[] {
|
|
14
|
+
const files: string[] = [];
|
|
15
|
+
|
|
16
|
+
for (const entry of readdirSync(directory, { withFileTypes: true })) {
|
|
17
|
+
const path = join(directory, entry.name);
|
|
18
|
+
if (entry.isDirectory()) files.push(...findFiles(path, extensions));
|
|
19
|
+
else if (extensions.some((extension) => entry.name.endsWith(extension))) files.push(path);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return files;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function read(path: string): string {
|
|
26
|
+
return readFileSync(path, 'utf8');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function displayPath(path: string): string {
|
|
30
|
+
return relative(repositoryRoot, path).replace(/\\/g, '/');
|
|
31
|
+
}
|
|
32
|
+
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { ALL_TOOLS } from '../tools';
|
|
3
|
+
import type { ToolLocaleContent } from '../types';
|
|
4
|
+
|
|
5
|
+
interface LinkFailure {
|
|
6
|
+
tool: string;
|
|
7
|
+
message: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function inspectEntry(tool: string, entry: { url: string }, seen: Set<string>): LinkFailure[] {
|
|
11
|
+
let url: URL;
|
|
12
|
+
try {
|
|
13
|
+
url = new URL(entry.url);
|
|
14
|
+
} catch {
|
|
15
|
+
return [{ tool, message: `invalid URL: ${entry.url}` }];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const failures: LinkFailure[] = [];
|
|
19
|
+
if (url.protocol !== 'https:') failures.push({ tool, message: `non-HTTPS URL: ${entry.url}` });
|
|
20
|
+
if (url.pathname === '/' && !url.search && !url.hash) failures.push({ tool, message: `generic homepage, cite the exact document: ${entry.url}` });
|
|
21
|
+
if (seen.has(url.href)) failures.push({ tool, message: `duplicate source URL: ${entry.url}` });
|
|
22
|
+
seen.add(url.href);
|
|
23
|
+
return failures;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function inspectTool(tool: typeof ALL_TOOLS[number]): Promise<LinkFailure[]> {
|
|
27
|
+
const loader = tool.entry.i18n.en;
|
|
28
|
+
if (!loader) return [{ tool: tool.entry.id, message: 'English locale loader is missing' }];
|
|
29
|
+
const content = (await loader()) as ToolLocaleContent;
|
|
30
|
+
const seen = new Set<string>();
|
|
31
|
+
return (content.bibliography ?? []).flatMap((entry) => inspectEntry(tool.entry.id, entry, seen));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
describe('QA: bibliography links are specific and usable', () => {
|
|
35
|
+
it('uses unique HTTPS links to exact source pages instead of generic homepages', async () => {
|
|
36
|
+
const failures: LinkFailure[] = [];
|
|
37
|
+
|
|
38
|
+
for (const tool of ALL_TOOLS) failures.push(...await inspectTool(tool));
|
|
39
|
+
|
|
40
|
+
const messages = failures.map(({ tool, message }) => `${tool}: ${message}`);
|
|
41
|
+
expect(messages, `Bibliography hygiene failures:\n${messages.join('\n')}`).toEqual([]);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { displayPath, listToolDirectories, read } from './qa-test-helpers';
|
|
5
|
+
|
|
6
|
+
const evidenceClaims = [
|
|
7
|
+
/\bofficial algorithm\b/i,
|
|
8
|
+
/\balgoritmo oficial\b/i,
|
|
9
|
+
/\b(?:records|data|rates|tables) updated(?:\s+(?:to|through)\s+20\d{2})?\b/i,
|
|
10
|
+
/\b(?:registros|datos|tasas|tablas) actualizad[oa]s?(?:\s+(?:a|hasta)\s+20\d{2})?\b/i,
|
|
11
|
+
/\b(?:validated|verified) (?:method|algorithm|calculation)\b/i,
|
|
12
|
+
/\b(?:método|algoritmo|cálculo) (?:validado|verificado)\b/i,
|
|
13
|
+
/\b(?:guarantees|ensures)\b/i,
|
|
14
|
+
/\bgarantiza\b/i,
|
|
15
|
+
/\bofficial (?:data|rate|calculation)\b/i,
|
|
16
|
+
/\b(?:datos|tasa|cálculo) oficial(?:es)?\b/i,
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
const requiredEvidenceFields = [
|
|
20
|
+
'reviewedAt',
|
|
21
|
+
'methodology',
|
|
22
|
+
'sources',
|
|
23
|
+
'referenceCases',
|
|
24
|
+
'limitations',
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
describe('QA: strong factual claims are traceable', () => {
|
|
28
|
+
it('requires machine-readable validation evidence beside tools making strong claims', () => {
|
|
29
|
+
const failures: string[] = [];
|
|
30
|
+
|
|
31
|
+
for (const directory of listToolDirectories()) {
|
|
32
|
+
const localePaths = ['en', 'es']
|
|
33
|
+
.map((locale) => join(directory, 'i18n', `${locale}.ts`))
|
|
34
|
+
.filter(existsSync);
|
|
35
|
+
const claims = localePaths.flatMap((path) => {
|
|
36
|
+
const source = read(path);
|
|
37
|
+
return evidenceClaims
|
|
38
|
+
.filter((pattern) => pattern.test(source))
|
|
39
|
+
.map((pattern) => `${displayPath(path)} matches ${pattern.source}`);
|
|
40
|
+
});
|
|
41
|
+
if (claims.length === 0) continue;
|
|
42
|
+
|
|
43
|
+
const evidencePath = join(directory, 'validation.ts');
|
|
44
|
+
if (!existsSync(evidencePath)) {
|
|
45
|
+
failures.push(`${displayPath(evidencePath)} missing; claims: ${claims.join(' | ')}`);
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const evidence = read(evidencePath);
|
|
50
|
+
const missingFields = requiredEvidenceFields.filter(
|
|
51
|
+
(field) => !new RegExp(`\\b${field}\\s*:`).test(evidence),
|
|
52
|
+
);
|
|
53
|
+
if (missingFields.length > 0) {
|
|
54
|
+
failures.push(`${displayPath(evidencePath)} missing fields: ${missingFields.join(', ')}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
expect(
|
|
59
|
+
failures,
|
|
60
|
+
[
|
|
61
|
+
'Claims such as official, validated, guaranteed or updated need inspectable evidence.',
|
|
62
|
+
'Add validation.ts with reviewedAt, methodology, sources, referenceCases and limitations,',
|
|
63
|
+
'or weaken/remove the unsupported claim.',
|
|
64
|
+
...failures,
|
|
65
|
+
].join('\n'),
|
|
66
|
+
).toEqual([]);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { basename, join } from 'node:path';
|
|
3
|
+
import { existsSync } from 'node:fs';
|
|
4
|
+
import {
|
|
5
|
+
displayPath,
|
|
6
|
+
findFiles,
|
|
7
|
+
listToolDirectories,
|
|
8
|
+
read,
|
|
9
|
+
repositoryRoot,
|
|
10
|
+
} from './qa-test-helpers';
|
|
11
|
+
|
|
12
|
+
function hasDedicatedLogicTest(toolDirectory: string): boolean {
|
|
13
|
+
const localCandidates = [
|
|
14
|
+
join(toolDirectory, 'logic.test.ts'),
|
|
15
|
+
join(toolDirectory, 'logic.spec.ts'),
|
|
16
|
+
join(toolDirectory, '__tests__', 'logic.test.ts'),
|
|
17
|
+
join(toolDirectory, '__tests__', 'logic.spec.ts'),
|
|
18
|
+
];
|
|
19
|
+
if (localCandidates.some(existsSync)) return true;
|
|
20
|
+
|
|
21
|
+
const toolName = basename(toolDirectory);
|
|
22
|
+
const centralTests = findFiles(join(repositoryRoot, 'src', 'tests'), ['.test.ts', '.spec.ts']);
|
|
23
|
+
return centralTests.some((testPath) => {
|
|
24
|
+
const source = read(testPath).replace(/\\/g, '/');
|
|
25
|
+
return source.includes(`/tool/${toolName}/logic`) || source.includes(`../tool/${toolName}/logic`);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
describe('QA: calculation logic has reference tests', () => {
|
|
30
|
+
it('gives every public calculator logic module its own behavioral test suite', () => {
|
|
31
|
+
const failures = listToolDirectories()
|
|
32
|
+
.filter((directory) => existsSync(join(directory, 'logic.ts')))
|
|
33
|
+
.filter((directory) => !hasDedicatedLogicTest(directory))
|
|
34
|
+
.map((directory) => `${displayPath(join(directory, 'logic.ts'))} -> no test imports this module`);
|
|
35
|
+
|
|
36
|
+
expect(
|
|
37
|
+
failures,
|
|
38
|
+
[
|
|
39
|
+
'Add behavioral tests through each logic module public API.',
|
|
40
|
+
'At minimum cover a documented reference case, boundaries, invalid input and invariants.',
|
|
41
|
+
...failures,
|
|
42
|
+
].join('\n'),
|
|
43
|
+
).toEqual([]);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { basename, join } from 'node:path';
|
|
3
|
+
import { existsSync } from 'node:fs';
|
|
4
|
+
import {
|
|
5
|
+
displayPath,
|
|
6
|
+
findFiles,
|
|
7
|
+
listToolDirectories,
|
|
8
|
+
read,
|
|
9
|
+
repositoryRoot,
|
|
10
|
+
} from './qa-test-helpers';
|
|
11
|
+
|
|
12
|
+
interface VisibleLiteral {
|
|
13
|
+
line: number;
|
|
14
|
+
value: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const domTextAssignment = /\.(?:innerText|textContent|innerHTML)\s*=/;
|
|
18
|
+
const quotedLiteral = /(['"`])((?:\\.|(?!\1).)*)\1/g;
|
|
19
|
+
|
|
20
|
+
function visibleWords(value: string): string {
|
|
21
|
+
return value
|
|
22
|
+
.replace(/\$\{[^}]*\}/g, ' ')
|
|
23
|
+
.replace(/<[^>]+>/g, ' ')
|
|
24
|
+
.replace(/&[a-z]+;/gi, ' ')
|
|
25
|
+
.replace(/[\d\p{P}\p{S}_]+/gu, ' ')
|
|
26
|
+
.replace(/\s+/g, ' ')
|
|
27
|
+
.trim();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const technicalLiteralPatterns = [
|
|
31
|
+
/^(?:\.|#|\[)/,
|
|
32
|
+
/^data-[a-z0-9-]+$/i,
|
|
33
|
+
/^(?:cc|ss)-copied$/i,
|
|
34
|
+
/^invalid-segments$/,
|
|
35
|
+
/^\(space\)$/,
|
|
36
|
+
/^(?:SHA-1|SHA-256|SHA-512|en-US)$/i,
|
|
37
|
+
/^(?:h|px|años)$/i,
|
|
38
|
+
/^\d+\s+[A-Z]$/,
|
|
39
|
+
/^(?:if \(|\{ key:)/,
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
function isTechnicalLiteral(value: string): boolean {
|
|
43
|
+
const trimmed = value.trim();
|
|
44
|
+
return value.includes('${t.')
|
|
45
|
+
|| value.includes('${ui.')
|
|
46
|
+
|| value.includes('${localized.')
|
|
47
|
+
|| technicalLiteralPatterns.some((pattern) => pattern.test(value) || pattern.test(trimmed))
|
|
48
|
+
|| (value.includes('${') && /(?:px|\bh\b|años)/i.test(value));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function hardcodedVisibleLiterals(source: string): VisibleLiteral[] {
|
|
52
|
+
const failures: VisibleLiteral[] = [];
|
|
53
|
+
|
|
54
|
+
source.split(/\r?\n/).forEach((line, index) => {
|
|
55
|
+
if (!domTextAssignment.test(line)) return;
|
|
56
|
+
|
|
57
|
+
const assignment = line.slice(line.search(domTextAssignment));
|
|
58
|
+
for (const match of assignment.matchAll(quotedLiteral)) {
|
|
59
|
+
if (isTechnicalLiteral(match[2])) continue;
|
|
60
|
+
const words = visibleWords(match[2]);
|
|
61
|
+
if (words && /\p{L}/u.test(words)) {
|
|
62
|
+
failures.push({ line: index + 1, value: match[2] });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
return failures;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function runtimeSources(toolDirectory: string): string {
|
|
71
|
+
return findFiles(toolDirectory, ['.astro', '.ts', '.js'])
|
|
72
|
+
.filter((path) => !path.includes(`${join(toolDirectory, 'i18n')}`))
|
|
73
|
+
.filter((path) => basename(path) !== 'ui.ts')
|
|
74
|
+
.map(read)
|
|
75
|
+
.join('\n');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
describe('QA: runtime copy is localized', () => {
|
|
79
|
+
const componentFiles = findFiles(join(repositoryRoot, 'src', 'tool'), ['.astro']);
|
|
80
|
+
|
|
81
|
+
it('does not write user-facing string literals directly into the DOM', () => {
|
|
82
|
+
const failures = componentFiles.flatMap((path) =>
|
|
83
|
+
hardcodedVisibleLiterals(read(path)).map(
|
|
84
|
+
({ line, value }) => `${displayPath(path)}:${line} -> ${JSON.stringify(value)}`,
|
|
85
|
+
),
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
expect(
|
|
89
|
+
failures,
|
|
90
|
+
`Move visible browser copy to the tool UI locale contract:\n${failures.join('\n')}`,
|
|
91
|
+
).toEqual([]);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('uses every string declared by each UI locale contract', () => {
|
|
95
|
+
const failures: string[] = [];
|
|
96
|
+
|
|
97
|
+
for (const directory of listToolDirectories()) {
|
|
98
|
+
const uiPath = join(directory, 'ui.ts');
|
|
99
|
+
if (!existsSync(uiPath)) continue;
|
|
100
|
+
|
|
101
|
+
const keys = Array.from(read(uiPath).matchAll(/^\s*(\w+)\??:\s*string\s*;/gm), (match) => match[1]);
|
|
102
|
+
const runtime = runtimeSources(directory);
|
|
103
|
+
const unused = keys.filter((key) => !new RegExp(`\\b${key}\\b`).test(runtime));
|
|
104
|
+
|
|
105
|
+
if (unused.length > 0) {
|
|
106
|
+
failures.push(`${displayPath(uiPath)} -> unused: ${unused.join(', ')}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
expect(
|
|
111
|
+
failures,
|
|
112
|
+
`Unused locale keys are dead copy or may mean the browser bypasses translations:\n${failures.join('\n')}`,
|
|
113
|
+
).toEqual([]);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
@@ -5,7 +5,7 @@ import { developerCategory } from '../data';
|
|
|
5
5
|
describe('Tool Validation Suite', () => {
|
|
6
6
|
describe('Library Registration', () => {
|
|
7
7
|
it('should have 28 tools in ALL_TOOLS', () => {
|
|
8
|
-
expect(ALL_TOOLS.length).toBe(
|
|
8
|
+
expect(ALL_TOOLS.length).toBe(29);
|
|
9
9
|
});
|
|
10
10
|
|
|
11
11
|
it('developerCategory should be defined', () => {
|
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
import { Bibliography as SharedBibliography } from '@jjlmoya/utils-shared';
|
|
3
|
-
import {
|
|
4
|
-
import type { KnownLocale } from '../../types';
|
|
5
|
-
|
|
6
|
-
interface Props {
|
|
7
|
-
locale?: KnownLocale;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const { locale = 'es' } = Astro.props;
|
|
11
|
-
const content = await aspectRatio.i18n[locale]?.();
|
|
3
|
+
import { bibliography } from './bibliography';
|
|
12
4
|
---
|
|
13
5
|
|
|
14
|
-
|
|
6
|
+
<SharedBibliography links={bibliography} />
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { BibliographyEntry } from '../../types';
|
|
2
|
+
|
|
3
|
+
export const bibliography: BibliographyEntry[] = [
|
|
4
|
+
{ name: 'MDN Web Docs: aspect-ratio (CSS)', url: 'https://developer.mozilla.org/en-US/docs/Web/CSS/aspect-ratio' },
|
|
5
|
+
{ name: 'Wikipedia: Aspect ratio', url: 'https://en.wikipedia.org/wiki/Aspect_ratio' },
|
|
6
|
+
{ name: 'W3C: CSS Box Sizing Level 4', url: 'https://www.w3.org/TR/css-sizing-4/#aspect-ratio' },
|
|
7
|
+
];
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { bibliography } from '../bibliography';
|
|
1
2
|
import type { WithContext, FAQPage, HowToThing, SoftwareApplication } from 'schema-dts';
|
|
2
3
|
import type { ToolLocaleContent } from '../../../types';
|
|
3
4
|
import type { AspectRatioUI } from '../ui';
|
|
@@ -109,20 +110,7 @@ export const content: ToolLocaleContent<AspectRatioUI> = {
|
|
|
109
110
|
faqTitle: 'Häufig gestellte Fragen',
|
|
110
111
|
faq: faqData,
|
|
111
112
|
bibliographyTitle: 'Referenzen und Dokumentation',
|
|
112
|
-
bibliography
|
|
113
|
-
{
|
|
114
|
-
name: 'MDN Web Docs: aspect-ratio (CSS)',
|
|
115
|
-
url: 'https://developer.mozilla.org/en-US/docs/Web/CSS/aspect-ratio',
|
|
116
|
-
},
|
|
117
|
-
{
|
|
118
|
-
name: 'Wikipedia: Seitenverhältnis',
|
|
119
|
-
url: 'https://de.wikipedia.org/wiki/Seitenverhältnis',
|
|
120
|
-
},
|
|
121
|
-
{
|
|
122
|
-
name: 'W3C: CSS Box Sizing Level 4',
|
|
123
|
-
url: 'https://www.w3.org/TR/css-sizing-4/#aspect-ratio',
|
|
124
|
-
},
|
|
125
|
-
],
|
|
113
|
+
bibliography,
|
|
126
114
|
howTo: howToData,
|
|
127
115
|
schemas: [faqSchema, howToSchema, appSchema],
|
|
128
116
|
seo: [
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { bibliography } from '../bibliography';
|
|
1
2
|
import type { WithContext, FAQPage, HowToThing, SoftwareApplication } from 'schema-dts';
|
|
2
3
|
import type { ToolLocaleContent } from '../../../types';
|
|
3
4
|
import type { AspectRatioUI } from '../ui';
|
|
@@ -109,20 +110,7 @@ export const content: ToolLocaleContent<AspectRatioUI> = {
|
|
|
109
110
|
faqTitle: 'Frequently Asked Questions',
|
|
110
111
|
faq: faqData,
|
|
111
112
|
bibliographyTitle: 'References and Documentation',
|
|
112
|
-
bibliography
|
|
113
|
-
{
|
|
114
|
-
name: 'MDN Web Docs: aspect-ratio (CSS)',
|
|
115
|
-
url: 'https://developer.mozilla.org/en-US/docs/Web/CSS/aspect-ratio',
|
|
116
|
-
},
|
|
117
|
-
{
|
|
118
|
-
name: 'Wikipedia: Aspect ratio',
|
|
119
|
-
url: 'https://en.wikipedia.org/wiki/Aspect_ratio',
|
|
120
|
-
},
|
|
121
|
-
{
|
|
122
|
-
name: 'W3C: CSS Box Sizing Level 4',
|
|
123
|
-
url: 'https://www.w3.org/TR/css-sizing-4/#aspect-ratio',
|
|
124
|
-
},
|
|
125
|
-
],
|
|
113
|
+
bibliography,
|
|
126
114
|
howTo: howToData,
|
|
127
115
|
schemas: [faqSchema, howToSchema, appSchema],
|
|
128
116
|
seo: [
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { bibliography } from '../bibliography';
|
|
1
2
|
import type { WithContext, FAQPage, HowToThing, SoftwareApplication } from 'schema-dts';
|
|
2
3
|
import type { ToolLocaleContent } from '../../../types';
|
|
3
4
|
import type { AspectRatioUI } from '../ui';
|
|
@@ -109,20 +110,7 @@ export const content: ToolLocaleContent<AspectRatioUI> = {
|
|
|
109
110
|
faqTitle: 'Preguntas Frecuentes',
|
|
110
111
|
faq: faqData,
|
|
111
112
|
bibliographyTitle: 'Referencias y Documentación',
|
|
112
|
-
bibliography
|
|
113
|
-
{
|
|
114
|
-
name: 'MDN Web Docs: aspect-ratio (CSS)',
|
|
115
|
-
url: 'https://developer.mozilla.org/es/docs/Web/CSS/aspect-ratio',
|
|
116
|
-
},
|
|
117
|
-
{
|
|
118
|
-
name: 'Wikipedia: Relación de aspecto',
|
|
119
|
-
url: 'https://es.wikipedia.org/wiki/Relaci%C3%B3n_de_aspecto',
|
|
120
|
-
},
|
|
121
|
-
{
|
|
122
|
-
name: 'W3C: CSS Box Sizing Level 4',
|
|
123
|
-
url: 'https://www.w3.org/TR/css-sizing-4/#aspect-ratio',
|
|
124
|
-
},
|
|
125
|
-
],
|
|
113
|
+
bibliography,
|
|
126
114
|
howTo: howToData,
|
|
127
115
|
schemas: [faqSchema, howToSchema, appSchema],
|
|
128
116
|
seo: [
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { bibliography } from '../bibliography';
|
|
1
2
|
import type { WithContext, FAQPage, HowToThing, SoftwareApplication } from 'schema-dts';
|
|
2
3
|
import type { ToolLocaleContent } from '../../../types';
|
|
3
4
|
import type { AspectRatioUI } from '../ui';
|
|
@@ -109,20 +110,7 @@ export const content: ToolLocaleContent<AspectRatioUI> = {
|
|
|
109
110
|
faqTitle: 'Questions Fréquentes',
|
|
110
111
|
faq: faqData,
|
|
111
112
|
bibliographyTitle: 'Références et Documentation',
|
|
112
|
-
bibliography
|
|
113
|
-
{
|
|
114
|
-
name: 'MDN Web Docs: aspect-ratio (CSS)',
|
|
115
|
-
url: 'https://developer.mozilla.org/fr/docs/Web/CSS/aspect-ratio',
|
|
116
|
-
},
|
|
117
|
-
{
|
|
118
|
-
name: 'Wikipedia: Format d\'image',
|
|
119
|
-
url: 'https://fr.wikipedia.org/wiki/Format_d%27image',
|
|
120
|
-
},
|
|
121
|
-
{
|
|
122
|
-
name: 'W3C: CSS Box Sizing Level 4',
|
|
123
|
-
url: 'https://www.w3.org/TR/css-sizing-4/#aspect-ratio',
|
|
124
|
-
},
|
|
125
|
-
],
|
|
113
|
+
bibliography,
|
|
126
114
|
howTo: howToData,
|
|
127
115
|
schemas: [faqSchema, howToSchema, appSchema],
|
|
128
116
|
seo: [
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { bibliography } from '../bibliography';
|
|
1
2
|
import type { WithContext, FAQPage, HowToThing, SoftwareApplication } from 'schema-dts';
|
|
2
3
|
import type { ToolLocaleContent } from '../../../types';
|
|
3
4
|
import type { AspectRatioUI } from '../ui';
|
|
@@ -109,20 +110,7 @@ export const content: ToolLocaleContent<AspectRatioUI> = {
|
|
|
109
110
|
faqTitle: 'Pertanyaan yang Sering Diajukan',
|
|
110
111
|
faq: faqData,
|
|
111
112
|
bibliographyTitle: 'Referensi dan Dokumentasi',
|
|
112
|
-
bibliography
|
|
113
|
-
{
|
|
114
|
-
name: 'MDN Web Docs: aspect-ratio (CSS)',
|
|
115
|
-
url: 'https://developer.mozilla.org/en-US/docs/Web/CSS/aspect-ratio',
|
|
116
|
-
},
|
|
117
|
-
{
|
|
118
|
-
name: 'Wikipedia: Aspect ratio',
|
|
119
|
-
url: 'https://en.wikipedia.org/wiki/Aspect_ratio',
|
|
120
|
-
},
|
|
121
|
-
{
|
|
122
|
-
name: 'W3C: CSS Box Sizing Level 4',
|
|
123
|
-
url: 'https://www.w3.org/TR/css-sizing-4/#aspect-ratio',
|
|
124
|
-
},
|
|
125
|
-
],
|
|
113
|
+
bibliography,
|
|
126
114
|
howTo: howToData,
|
|
127
115
|
schemas: [faqSchema, howToSchema, appSchema],
|
|
128
116
|
seo: [
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { bibliography } from '../bibliography';
|
|
1
2
|
import type { WithContext, FAQPage, HowToThing, SoftwareApplication } from 'schema-dts';
|
|
2
3
|
import type { ToolLocaleContent } from '../../../types';
|
|
3
4
|
import type { AspectRatioUI } from '../ui';
|
|
@@ -109,20 +110,7 @@ export const content: ToolLocaleContent<AspectRatioUI> = {
|
|
|
109
110
|
faqTitle: 'Domande frequenti',
|
|
110
111
|
faq: faqData,
|
|
111
112
|
bibliographyTitle: 'Riferimenti e documentazione',
|
|
112
|
-
bibliography
|
|
113
|
-
{
|
|
114
|
-
name: 'MDN Web Docs: aspect-ratio (CSS)',
|
|
115
|
-
url: 'https://developer.mozilla.org/en-US/docs/Web/CSS/aspect-ratio',
|
|
116
|
-
},
|
|
117
|
-
{
|
|
118
|
-
name: 'Wikipedia: Aspect ratio',
|
|
119
|
-
url: 'https://it.wikipedia.org/wiki/Rapporto_d\'aspetto',
|
|
120
|
-
},
|
|
121
|
-
{
|
|
122
|
-
name: 'W3C: CSS Box Sizing Level 4',
|
|
123
|
-
url: 'https://www.w3.org/TR/css-sizing-4/#aspect-ratio',
|
|
124
|
-
},
|
|
125
|
-
],
|
|
113
|
+
bibliography,
|
|
126
114
|
howTo: howToData,
|
|
127
115
|
schemas: [faqSchema, howToSchema, appSchema],
|
|
128
116
|
seo: [
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { bibliography } from '../bibliography';
|
|
1
2
|
import type { WithContext, FAQPage, HowToThing, SoftwareApplication } from 'schema-dts';
|
|
2
3
|
import type { ToolLocaleContent } from '../../../types';
|
|
3
4
|
import type { AspectRatioUI } from '../ui';
|
|
@@ -109,20 +110,7 @@ export const content: ToolLocaleContent<AspectRatioUI> = {
|
|
|
109
110
|
faqTitle: 'よくある質問',
|
|
110
111
|
faq: faqData,
|
|
111
112
|
bibliographyTitle: '参考文献とドキュメント',
|
|
112
|
-
bibliography
|
|
113
|
-
{
|
|
114
|
-
name: 'MDN Web Docs: aspect-ratio (CSS)',
|
|
115
|
-
url: 'https://developer.mozilla.org/en-US/docs/Web/CSS/aspect-ratio',
|
|
116
|
-
},
|
|
117
|
-
{
|
|
118
|
-
name: 'Wikipedia: アスペクト比',
|
|
119
|
-
url: 'https://ja.wikipedia.org/wiki/アスペクト比',
|
|
120
|
-
},
|
|
121
|
-
{
|
|
122
|
-
name: 'W3C: CSS Box Sizing Level 4',
|
|
123
|
-
url: 'https://www.w3.org/TR/css-sizing-4/#aspect-ratio',
|
|
124
|
-
},
|
|
125
|
-
],
|
|
113
|
+
bibliography,
|
|
126
114
|
howTo: howToData,
|
|
127
115
|
schemas: [faqSchema, howToSchema, appSchema],
|
|
128
116
|
seo: [
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { bibliography } from '../bibliography';
|
|
1
2
|
import type { WithContext, FAQPage, HowToThing, SoftwareApplication } from 'schema-dts';
|
|
2
3
|
import type { ToolLocaleContent } from '../../../types';
|
|
3
4
|
import type { AspectRatioUI } from '../ui';
|
|
@@ -109,20 +110,7 @@ export const content: ToolLocaleContent<AspectRatioUI> = {
|
|
|
109
110
|
faqTitle: '자주 묻는 질문',
|
|
110
111
|
faq: faqData,
|
|
111
112
|
bibliographyTitle: '참고 자료 및 문서',
|
|
112
|
-
bibliography
|
|
113
|
-
{
|
|
114
|
-
name: 'MDN Web Docs: aspect-ratio (CSS)',
|
|
115
|
-
url: 'https://developer.mozilla.org/en-US/docs/Web/CSS/aspect-ratio',
|
|
116
|
-
},
|
|
117
|
-
{
|
|
118
|
-
name: 'Wikipedia: Aspect ratio',
|
|
119
|
-
url: 'https://ko.wikipedia.org/wiki/가로세로비',
|
|
120
|
-
},
|
|
121
|
-
{
|
|
122
|
-
name: 'W3C: CSS Box Sizing Level 4',
|
|
123
|
-
url: 'https://www.w3.org/TR/css-sizing-4/#aspect-ratio',
|
|
124
|
-
},
|
|
125
|
-
],
|
|
113
|
+
bibliography,
|
|
126
114
|
howTo: howToData,
|
|
127
115
|
schemas: [faqSchema, howToSchema, appSchema],
|
|
128
116
|
seo: [
|