1*082815dcSEvan Baconimport fs from 'fs'; 2*082815dcSEvan Baconimport { vol } from 'memfs'; 3*082815dcSEvan Bacon 4*082815dcSEvan Baconimport { buildResourceItem, readResourcesXMLAsync } from '../../android/Resources'; 5*082815dcSEvan Baconimport { setStringItem } from '../../android/Strings'; 6*082815dcSEvan Baconimport { escapeAndroidString, format, unescapeAndroidString, writeXMLAsync } from '../XML'; 7*082815dcSEvan Bacon 8*082815dcSEvan Baconjest.mock('fs'); 9*082815dcSEvan Bacon 10*082815dcSEvan Baconexport const sampleStringsXML = ` 11*082815dcSEvan Bacon<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 12*082815dcSEvan Bacon<resources> 13*082815dcSEvan Bacon <string name="app_name">exp\\'o &bo<y>'</string> 14*082815dcSEvan Bacon</resources>`; 15*082815dcSEvan Bacon 16*082815dcSEvan Bacondescribe(readResourcesXMLAsync, () => { 17*082815dcSEvan Bacon beforeAll(async () => { 18*082815dcSEvan Bacon const directoryJSON = { 19*082815dcSEvan Bacon './android/app/src/main/res/values/strings.xml': sampleStringsXML, 20*082815dcSEvan Bacon }; 21*082815dcSEvan Bacon vol.fromJSON(directoryJSON, '/app'); 22*082815dcSEvan Bacon }); 23*082815dcSEvan Bacon 24*082815dcSEvan Bacon afterAll(async () => { 25*082815dcSEvan Bacon vol.reset(); 26*082815dcSEvan Bacon }); 27*082815dcSEvan Bacon 28*082815dcSEvan Bacon it(`can write the escaped name and then read it back in unescaped format`, async () => { 29*082815dcSEvan Bacon const stringsPath = '/app/android/app/src/main/res/values/strings.xml'; 30*082815dcSEvan Bacon let stringsJSON = await readResourcesXMLAsync({ path: stringsPath }); 31*082815dcSEvan Bacon expect(stringsJSON.resources.string.filter((e) => e.$.name === 'app_name')[0]._).toBe( 32*082815dcSEvan Bacon `exp'o &bo<y>'` 33*082815dcSEvan Bacon ); 34*082815dcSEvan Bacon stringsJSON = setStringItem( 35*082815dcSEvan Bacon [buildResourceItem({ name: 'app_name', value: `'E&x<p>o"@\n` })], 36*082815dcSEvan Bacon stringsJSON 37*082815dcSEvan Bacon ); 38*082815dcSEvan Bacon 39*082815dcSEvan Bacon // Test that it's written in escaped form 40*082815dcSEvan Bacon // expect(format(stringsJSON)).toBe(true); 41*082815dcSEvan Bacon expect(format(stringsJSON).includes(`\\'E&x<p>o\\"\\@\\n`)).toBe(true); 42*082815dcSEvan Bacon 43*082815dcSEvan Bacon // And parsed in unescaped form 44*082815dcSEvan Bacon expect(stringsJSON.resources.string.filter((e) => e.$.name === 'app_name')[0]._).toBe( 45*082815dcSEvan Bacon `\\'E&x<p>o\\"\\@\\n` 46*082815dcSEvan Bacon ); 47*082815dcSEvan Bacon }); 48*082815dcSEvan Bacon}); 49*082815dcSEvan Bacon 50*082815dcSEvan Bacondescribe('read and write', () => { 51*082815dcSEvan Bacon // reading removes 52*082815dcSEvan Bacon // <?xml version="1.0" encoding="UTF-8" standalone="yes"?> 53*082815dcSEvan Bacon const example = `<resources> 54*082815dcSEvan Bacon <string name="app_name">exp\\'o</string> 55*082815dcSEvan Bacon</resources>`; 56*082815dcSEvan Bacon beforeAll(async () => { 57*082815dcSEvan Bacon const directoryJSON = { 58*082815dcSEvan Bacon './android/app/src/main/res/values/strings.xml': example, 59*082815dcSEvan Bacon }; 60*082815dcSEvan Bacon vol.fromJSON(directoryJSON, '/app'); 61*082815dcSEvan Bacon }); 62*082815dcSEvan Bacon 63*082815dcSEvan Bacon afterAll(async () => { 64*082815dcSEvan Bacon vol.reset(); 65*082815dcSEvan Bacon }); 66*082815dcSEvan Bacon 67*082815dcSEvan Bacon it(`can write the escaped name and then read it back in unescaped format`, async () => { 68*082815dcSEvan Bacon const stringsPath = '/app/android/app/src/main/res/values/strings.xml'; 69*082815dcSEvan Bacon const stringsJSON = await readResourcesXMLAsync({ path: stringsPath }); 70*082815dcSEvan Bacon await writeXMLAsync({ path: stringsPath, xml: stringsJSON }); 71*082815dcSEvan Bacon expect(await fs.promises.readFile(stringsPath, 'utf-8')).toBe(example); 72*082815dcSEvan Bacon }); 73*082815dcSEvan Bacon}); 74*082815dcSEvan Bacon 75*082815dcSEvan Bacondescribe(escapeAndroidString, () => { 76*082815dcSEvan Bacon it(`can escape Android strings`, () => { 77*082815dcSEvan Bacon expect(escapeAndroidString(`@`)).toBe(`\\@`); 78*082815dcSEvan Bacon expect(escapeAndroidString(`'''`)).toBe(`\\'\\'\\'`); 79*082815dcSEvan Bacon expect(escapeAndroidString('"E&x<p>o"@\n\r\t')).toBe(`\\"E&x<p>o\\"\\@\\n\\r\\t`); 80*082815dcSEvan Bacon }); 81*082815dcSEvan Bacon}); 82*082815dcSEvan Bacon 83*082815dcSEvan Bacondescribe(unescapeAndroidString, () => { 84*082815dcSEvan Bacon it(`can remove escape sequences from Android strings`, () => { 85*082815dcSEvan Bacon expect(unescapeAndroidString(`test\\test`)).toBe('testtest'); 86*082815dcSEvan Bacon expect(unescapeAndroidString(`test\\'test`)).toBe("test'test"); 87*082815dcSEvan Bacon expect(unescapeAndroidString(`test\\\\'test`)).toBe("test\\'test"); 88*082815dcSEvan Bacon }); 89*082815dcSEvan Bacon}); 90