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 &amp;bo&lt;y&gt;&apos;</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&amp;x&lt;p&gt;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