1import { render } from '@testing-library/react';
2import GithubSlugger from 'github-slugger';
3import * as React from 'react';
4
5import AppConfigSchemaPropertiesTable, {
6  formatSchema,
7  createDescription,
8  _getType,
9  Property,
10} from './AppConfigSchemaPropertiesTable';
11
12import { HeadingManager } from '~/common/headingManager';
13import { HeadingsContext } from '~/components/page-higher-order/withHeadingManager';
14
15const testSchema: Record<string, Property> = {
16  name: {
17    description: 'Name of your app.',
18    type: 'string',
19    meta: {
20      bareWorkflow: "Edit the 'Display Name' field in Xcode",
21    },
22  },
23  androidNavigationBar: {
24    description: 'Configuration for the bottom navigation bar on Android.',
25    type: 'object',
26    properties: {
27      visible: {
28        description: 'Determines how and when the navigation bar is shown.',
29        type: 'string',
30        properties: {
31          always: {
32            description: 'Test sub-sub-property',
33            type: 'boolean',
34          },
35        },
36        meta: {
37          expoKit: 'Set this property using Xcode.',
38        },
39        enum: ['leanback', 'immersive', 'sticky-immersive'],
40      },
41      backgroundColor: {
42        description: 'Specifies the background color of the navigation bar. ',
43        type: 'string',
44        pattern: '^#|(&#x23;)\\d{6}$',
45        meta: {
46          regexHuman: "6 character long hex color string, eg: `'#000000'`",
47        },
48      },
49    },
50    meta: {
51      expoKit: 'Set this property using AppConstants.java.',
52      bareWorkflow: 'Set this property using just Xcode',
53    },
54  },
55  intentFilters: {
56    description: 'Configuration for setting an array of custom intent filters in Android manifest.',
57    example: [
58      {
59        autoVerify: true,
60        data: {
61          host: '*.expo.io',
62        },
63      },
64    ],
65    exampleString: '\n [{ \n "autoVerify": true, \n "data": {"host": "*.expo.io" \n } \n }]',
66    type: 'array',
67    uniqueItems: true,
68    items: {
69      type: 'object',
70      properties: {
71        autoVerify: {
72          description:
73            'You may also use an intent filter to set your app as the default handler for links',
74          type: 'boolean',
75        },
76        data: {
77          type: ['array', 'object'],
78          items: {
79            type: 'object',
80            properties: {
81              host: { description: 'the host, e.g. `myapp.io`', type: 'string' },
82            },
83            additionalProperties: false,
84          },
85          properties: {
86            host: { type: 'string' },
87          },
88          additionalProperties: false,
89        },
90      },
91      additionalProperties: false,
92      required: ['action'],
93    },
94    meta: {
95      bareWorkflow: 'This is set in AndroidManifest.xml directly.',
96    },
97  },
98};
99
100describe('AppConfigSchemaPropertiesTable', () => {
101  test('correctly matches snapshot', () => {
102    const { container } = render(
103      <HeadingsContext.Provider value={new HeadingManager(new GithubSlugger(), {})}>
104        <AppConfigSchemaPropertiesTable schema={testSchema} />
105      </HeadingsContext.Provider>
106    );
107    expect(container).toMatchSnapshot();
108  });
109});
110
111describe('formatSchema', () => {
112  const formattedSchema = formatSchema(Object.entries(testSchema));
113  test('name is property nestingLevel 0', () => {
114    expect(formattedSchema[0].nestingLevel).toBe(0);
115  });
116  test('androidNavigationBar is property nestingLevel 0', () => {
117    expect(formattedSchema[1].nestingLevel).toBe(0);
118  });
119  test('visible is subproperty nestingLevel 1', () => {
120    expect(formattedSchema[2].nestingLevel).toBe(1);
121  });
122  test('always is subproperty nestingLevel 2', () => {
123    expect(formattedSchema[3].nestingLevel).toBe(2);
124  });
125  test('backgroundColor is subproperty nestingLevel 1', () => {
126    expect(formattedSchema[4].nestingLevel).toBe(1);
127  });
128  test('intentFilters is property nestingLevel 0', () => {
129    expect(formattedSchema[5].nestingLevel).toBe(0);
130  });
131  test('autoVerify is subproperty nestingLevel 1', () => {
132    expect(formattedSchema[6].nestingLevel).toBe(1);
133  });
134  test('data is subproperty nestingLevel 1', () => {
135    expect(formattedSchema[7].nestingLevel).toBe(1);
136  });
137  test('host is subproperty nestingLevel 2', () => {
138    expect(formattedSchema[8].nestingLevel).toBe(2);
139  });
140});
141
142describe('createDescription', () => {
143  test('bareWorkflow, exampleString are both added correctly to intentFilters', () => {
144    const intentFiltersObject = Object.entries(testSchema)[2];
145    const intentFiltersObjectValue = intentFiltersObject[1] as any;
146    const result = createDescription(intentFiltersObject);
147
148    expect(result).toBe(
149      `**(${_getType(intentFiltersObjectValue)})** - ${
150        intentFiltersObjectValue.description
151      }<bareworkflowDetails>${
152        intentFiltersObjectValue.meta!.bareWorkflow
153      }</bareworkflowDetails>\n\n>${intentFiltersObjectValue.exampleString}`
154    );
155  });
156
157  test('regexHuman is added correctly to backgroundColor', () => {
158    //Note: to access this subproperty is tedious without a call to formatSchema
159    const backgroundColorObject = Object.entries(Object.values(testSchema)[1].properties!)[1];
160    const backgroundColorObjectValue = backgroundColorObject[1];
161    const result = createDescription(backgroundColorObject);
162
163    expect(result).toBe(
164      `**(${_getType(backgroundColorObjectValue)})** - ${
165        backgroundColorObjectValue.description
166      }\n\n${backgroundColorObjectValue.meta!.regexHuman}`
167    );
168  });
169
170  test('expoKit is added correctly to visible', () => {
171    //Note: to access this subproperty is tedious without a call to formatSchema
172    const visibleObject = Object.entries(Object.values(testSchema)[1].properties!)[0];
173    const visibleObjectValue = visibleObject[1];
174    const result = createDescription(visibleObject);
175
176    expect(result).toBe(
177      `**(${_getType(visibleObjectValue)})** - ${visibleObjectValue.description}<expokitDetails>${
178        visibleObjectValue.meta!.expoKit
179      }</expokitDetails>`
180    );
181  });
182
183  test('bareWorkflow is added correctly to name', () => {
184    const nameObject = Object.entries(testSchema)[0];
185    const nameObjectValue = nameObject[1];
186    const result = createDescription(nameObject);
187
188    expect(result).toBe(
189      `**(${_getType(nameObjectValue)})** - ${nameObjectValue.description}<bareworkflowDetails>${
190        nameObjectValue.meta!.bareWorkflow
191      }</bareworkflowDetails>`
192    );
193  });
194
195  test('expoKit, bareWorkflow both added correctly to androidNavigationBar', () => {
196    const androidNavigationBarObject = Object.entries(testSchema)[1];
197    const androidNavigationBarObjectValue = androidNavigationBarObject[1] as any;
198    const result = createDescription(androidNavigationBarObject);
199
200    expect(result).toBe(
201      `**(${_getType(androidNavigationBarObjectValue)})** - ${
202        androidNavigationBarObjectValue.description
203      }<expokitDetails>${
204        androidNavigationBarObjectValue.meta!.expoKit
205      }</expokitDetails><bareworkflowDetails>${
206        androidNavigationBarObjectValue.meta!.bareWorkflow
207      }</bareworkflowDetails>`
208    );
209  });
210});
211