diff --git a/.maestro/enrichedInput/flows/link_href_attribute_escaping.yaml b/.maestro/enrichedInput/flows/link_href_attribute_escaping.yaml new file mode 100644 index 000000000..56d5712cb --- /dev/null +++ b/.maestro/enrichedInput/flows/link_href_attribute_escaping.yaml @@ -0,0 +1,73 @@ +appId: swmansion.enriched.example +--- +# Regression test for href="..." attribute-injection: a URL containing " (or +# &) must be HTML-attribute-escaped on serialize, and the escaped output must +# decode back to the original, un-escaped URL when re-parsed. +- launchApp + +- tapOn: + id: 'toggle-screen-button' + +- tapOn: + id: 'editor-input' + +- inputText: 'Hello ' + +- runFlow: + file: '../subflows/insert_link.yaml' + env: + LINK_TEXT: 'test' + LINK_URL: 'https://example.com/a&b"c' + +- extendedWaitUntil: + notVisible: + id: 'link-modal-submit-button' + timeout: 10000 + +- tapOn: + id: 'toggle-html-button' + +# the raw " and & must not reach the output unescaped (that's the +# attribute-injection bug: it would break out of href="...") +- assertVisible: + id: 'html-output' + text: '.*href="https://example.com/a&b"c".*' + +- assertNotVisible: + id: 'html-output' + text: '.*href="https://example.com/a&b"c".*' + +# Restart the app before the round-trip check below. On iOS, presenting a +# second Modal right after dismissing one (LinkModal -> ValueModal) can leave +# the accessibility snapshot stuck empty indefinitely - the screen still +# renders correctly, but Maestro can't see any element in it. A full relaunch +# resets the native view/accessibility state; losing the editor content here +# is fine since the next step overwrites it anyway via "Set Value". +- launchApp + +- tapOn: + id: 'toggle-screen-button' + +# Feed the escaped HTML back in. The round trip is verified via the HTML +# output box rather than by reopening the link modal to read LinkData.url: +# reopening it is a second Modal in this same session and hits the exact +# accessibility-snapshot issue above. Checking the re-serialized HTML instead +# proves the same thing - decode must turn "&b"c" back into the raw +# "&b"c" that re-encode then escapes right back to "&b"c" - without +# ever presenting a second Modal. +- runFlow: + file: '../subflows/set_editor_value.yaml' + env: + VALUE: '

test

' + +- tapOn: + id: 'toggle-html-button' + +- assertVisible: + id: 'html-output' + text: '.*href="https://example.com/a&b"c".*' + +# if decode left the entities un-decoded, re-encoding would double-escape them +- assertNotVisible: + id: 'html-output' + text: '.*href="https://example.com/a&.*' diff --git a/.maestro/enrichedInput/screenshots/ios/mention_popup_closing_on_cursor_travel_1.png b/.maestro/enrichedInput/screenshots/ios/mention_popup_closing_on_cursor_travel_1.png index 650394951..84c65f7b9 100644 Binary files a/.maestro/enrichedInput/screenshots/ios/mention_popup_closing_on_cursor_travel_1.png and b/.maestro/enrichedInput/screenshots/ios/mention_popup_closing_on_cursor_travel_1.png differ diff --git a/.maestro/enrichedInput/screenshots/ios/mention_popup_closing_on_cursor_travel_2.png b/.maestro/enrichedInput/screenshots/ios/mention_popup_closing_on_cursor_travel_2.png index a50059814..2d8ebc5c8 100644 Binary files a/.maestro/enrichedInput/screenshots/ios/mention_popup_closing_on_cursor_travel_2.png and b/.maestro/enrichedInput/screenshots/ios/mention_popup_closing_on_cursor_travel_2.png differ diff --git a/.maestro/enrichedInput/screenshots/ios/mention_popup_closing_on_cursor_travel_3.png b/.maestro/enrichedInput/screenshots/ios/mention_popup_closing_on_cursor_travel_3.png index dd2d16428..7ed8418be 100644 Binary files a/.maestro/enrichedInput/screenshots/ios/mention_popup_closing_on_cursor_travel_3.png and b/.maestro/enrichedInput/screenshots/ios/mention_popup_closing_on_cursor_travel_3.png differ diff --git a/.maestro/enrichedText/flows/image_attribute_entity_decoding.yaml b/.maestro/enrichedText/flows/image_attribute_entity_decoding.yaml new file mode 100644 index 000000000..0ae4f1fec --- /dev/null +++ b/.maestro/enrichedText/flows/image_attribute_entity_decoding.yaml @@ -0,0 +1,26 @@ +appId: swmansion.enriched.example +--- +# Entities inside src="..." must decode too - regression test for the src +# attribute-escaping fix. +- launchApp + +- tapOn: + id: 'toggle-screen-button' + +- tapOn: + id: 'toggle-enriched-text-screen-button' + +- runFlow: + file: '../subflows/set_enriched_text_value.yaml' + env: + VALUE: '' + +- tapOn: + id: 'enriched-text' + point: '5%,50%' + +- assertVisible: + text: 'You pressed the image:.*img&x.png.*' + +- assertNotVisible: + text: '.*&.*' diff --git a/.maestro/enrichedText/flows/link_attribute_entity_decoding.yaml b/.maestro/enrichedText/flows/link_attribute_entity_decoding.yaml new file mode 100644 index 000000000..e7eb0c1fe --- /dev/null +++ b/.maestro/enrichedText/flows/link_attribute_entity_decoding.yaml @@ -0,0 +1,26 @@ +appId: swmansion.enriched.example +--- +# Entities inside href="..." must decode too, not just inside text content - +# regression test for the href attribute-escaping fix. +- launchApp + +- tapOn: + id: 'toggle-screen-button' + +- tapOn: + id: 'toggle-enriched-text-screen-button' + +- runFlow: + file: '../subflows/set_enriched_text_value.yaml' + env: + VALUE: '

link

' + +- tapOn: + id: 'enriched-text' + point: '5%,50%' + +- assertVisible: + text: 'You pressed the link: https://example.com/a&b"c' + +- assertNotVisible: + text: '.*&(amp|quot);.*' diff --git a/.maestro/enrichedText/flows/mention_attribute_entity_decoding.yaml b/.maestro/enrichedText/flows/mention_attribute_entity_decoding.yaml new file mode 100644 index 000000000..f7e80ad12 --- /dev/null +++ b/.maestro/enrichedText/flows/mention_attribute_entity_decoding.yaml @@ -0,0 +1,27 @@ +appId: swmansion.enriched.example +--- +# Entities inside mention attributes (a JSON blob spliced into key="value" +# pairs) must decode too - regression test for the mention attribute-escaping +# fix. +- launchApp + +- tapOn: + id: 'toggle-screen-button' + +- tapOn: + id: 'toggle-enriched-text-screen-button' + +- runFlow: + file: '../subflows/set_enriched_text_value.yaml' + env: + VALUE: '

@John Doe

' + +- tapOn: + id: 'enriched-text' + point: '5%,50%' + +- assertVisible: + text: 'You pressed the mention:.*"id":"1&2".*' + +- assertNotVisible: + text: '.*&.*' diff --git a/.maestro/enrichedText/flows/named_html_entities.yaml b/.maestro/enrichedText/flows/named_html_entities.yaml new file mode 100644 index 000000000..9d5ce4ae6 --- /dev/null +++ b/.maestro/enrichedText/flows/named_html_entities.yaml @@ -0,0 +1,23 @@ +appId: swmansion.enriched.example +--- +# Issue #818 - named character references ( , ", ', ...) are +# decoded instead of being rendered literally. +- launchApp + +- tapOn: + id: 'toggle-screen-button' + +- tapOn: + id: 'toggle-enriched-text-screen-button' + +- runFlow: + file: '../subflows/set_enriched_text_value.yaml' + env: + VALUE: "

Hello world "q" 'a' —

" + +# '.' matches the decoded U+00A0 between "Hello" and "world" +- assertVisible: + text: "Hello.world \"q\" 'a' —" + +- assertNotVisible: + text: '.*&(nbsp|quot|apos|mdash);.*' diff --git a/apps/example/src/screens/TestScreen.tsx b/apps/example/src/screens/TestScreen.tsx index 1e64d5225..85266b36b 100644 --- a/apps/example/src/screens/TestScreen.tsx +++ b/apps/example/src/screens/TestScreen.tsx @@ -7,6 +7,7 @@ import { LinkModal } from '../components/LinkModal'; import { MentionPopup } from '../components/MentionPopup'; import { ImageModal } from '../components/ImageModal'; import { ValueModal } from '../components/ValueModal'; +import { HtmlSection } from '../components/HtmlSection'; import { useEditorState } from '../hooks/useEditorState'; import { useKeyboardHeight } from '../hooks/useKeyboardHeight'; import { @@ -111,6 +112,7 @@ export function TestScreen({ layout="grid" /> +