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