Skip to content
This repository has been archived by the owner on Jan 18, 2024. It is now read-only.

Commit

Permalink
[schemer] [ENG-9956]: validate for unsupported images (#4764)
Browse files Browse the repository at this point in the history
* Add test for invlid app icon path

* Convert tests to ts and tidy assertions

* Ensure schema errors for local webp images

* Ensure tests check for errors

* Validate for file extension mismatch

* Fix test for remote image

* Add changelog entry
  • Loading branch information
kadikraman committed Sep 27, 2023
1 parent a100d1b commit e1f5432
Show file tree
Hide file tree
Showing 15 changed files with 560 additions and 266 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ This is the log of notable changes to Expo CLI and related packages.
- [create-expo] Bump @expo/package-manager for Bun support
- [create-expo] detect bun package manager ([#4752](https://github.com/expo/expo-cli/issues/4752))
- [webpack]: Bump expo to SDK 49 ([#4747](https://github.com/expo/expo-cli/issues/4747))
- [schemer]: additional validation for unsupported image formats ([#4764](https://github.com/expo/expo-cli/pull/4764))

### 🧹 Chores

Expand Down
21 changes: 21 additions & 0 deletions packages/schemer/__tests__/__snapshots__/network-test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Remote Remote icon dimensions wrong 1`] = `
Array [
Object {
"data": "https://httpbin.org/image/png",
"errorCode": "INVALID_DIMENSIONS",
"fieldPath": "icon",
"message": "'icon' should have dimensions 101x100, but the file at 'https://httpbin.org/image/png' has dimensions 100x100",
"meta": Object {
"asset": true,
"contentTypePattern": "^image/png$",
"dimensions": Object {
"height": 100,
"width": 101,
},
},
"name": "ValidationError",
},
]
`;
77 changes: 0 additions & 77 deletions packages/schemer/__tests__/__snapshots__/test.js.snap

This file was deleted.

196 changes: 196 additions & 0 deletions packages/schemer/__tests__/__snapshots__/test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Holistic Unit Test bad example app.json - invalid path for app icon 1`] = `
Array [
Object {
"data": "./unknown/path.png",
"errorCode": "INVALID_ASSET_URI",
"fieldPath": "icon",
"message": "cannot access file at './unknown/path.png'",
"meta": Object {
"asset": true,
"bareWorkflow": "To change your app's icon, edit or replace the files in \`ios/<PROJECT-NAME>/Assets.xcassets/AppIcon.appiconset\` (we recommend using Xcode), and \`android/app/src/main/res/mipmap-<RESOLUTION>\`. Be sure to follow the guidelines for each platform ([iOS](https://developer.apple.com/design/human-interface-guidelines/ios/icons-and-images/app-icon/), [Android 7.1 and below](https://material.io/design/iconography/#icon-treatments), and [Android 8+](https://developer.android.com/guide/practices/ui_guidelines/icon_design_adaptive)) and to provide your new icon in each existing size.",
"contentTypeHuman": ".png image",
"contentTypePattern": "^image/png$",
"square": true,
},
"name": "ValidationError",
},
]
`;

exports[`Holistic Unit Test bad example app.json schema 1`] = `
Array [
Object {
"data": Object {
"asdfasdfandroid": Object {
"package": "com.yourcompany.yourappname",
},
"icon": "DoesNotExist.png",
"orientaasdfasdftion": "portrait",
"sdkVersion": "17.0.0abad",
"slug": "1*@)#($*@)(#$*)",
},
"errorCode": "SCHEMA_MISSING_REQUIRED_PROPERTY",
"fieldPath": "",
"message": "is missing required property 'name'",
"meta": undefined,
"name": "ValidationError",
},
Object {
"data": Object {
"asdfasdfandroid": Object {
"package": "com.yourcompany.yourappname",
},
"icon": "DoesNotExist.png",
"orientaasdfasdftion": "portrait",
"sdkVersion": "17.0.0abad",
"slug": "1*@)#($*@)(#$*)",
},
"errorCode": "SCHEMA_ADDITIONAL_PROPERTY",
"fieldPath": "",
"message": "should NOT have additional property 'orientaasdfasdftion'",
"meta": undefined,
"name": "ValidationError",
},
Object {
"data": Object {
"asdfasdfandroid": Object {
"package": "com.yourcompany.yourappname",
},
"icon": "DoesNotExist.png",
"orientaasdfasdftion": "portrait",
"sdkVersion": "17.0.0abad",
"slug": "1*@)#($*@)(#$*)",
},
"errorCode": "SCHEMA_ADDITIONAL_PROPERTY",
"fieldPath": "",
"message": "should NOT have additional property 'asdfasdfandroid'",
"meta": undefined,
"name": "ValidationError",
},
Object {
"data": "1*@)#($*@)(#$*)",
"errorCode": "SCHEMA_INVALID_PATTERN",
"fieldPath": "slug",
"message": "'slug' must match pattern \\"^[a-zA-Z0-9_\\\\-]+$\\"",
"meta": undefined,
"name": "ValidationError",
},
]
`;

exports[`Holistic Unit Test bad example app.json schema with field with not 1`] = `
Array [
Object {
"data": "1.0",
"errorCode": "SCHEMA_INVALID_NOT",
"fieldPath": "runtimeVersion",
"message": "'runtimeVersion' should be not a decimal ending in a 0.",
"meta": Object {
"notHuman": "Not a decimal ending in a 0.",
},
"name": "ValidationError",
},
]
`;

exports[`Image Validation errors for webp images 1`] = `
Array [
Object {
"data": "./files/webp.webp",
"errorCode": "INVALID_CONTENT_TYPE",
"fieldPath": "Android.adaptiveIcon.foregroundImage",
"message": "field 'Android.adaptiveIcon.foregroundImage' should point to .png image but the file at './files/webp.webp' has type webp",
"meta": Object {
"asset": true,
"contentTypeHuman": ".png image",
"contentTypePattern": "^image/png$",
"square": true,
},
"name": "ValidationError",
},
Object {
"data": "./files/webp.webp",
"errorCode": "NOT_SQUARE",
"fieldPath": "Android.adaptiveIcon.foregroundImage",
"message": "image should be square, but the file at './files/webp.webp' has dimensions 320x214",
"meta": Object {
"asset": true,
"contentTypeHuman": ".png image",
"contentTypePattern": "^image/png$",
"square": true,
},
"name": "ValidationError",
},
]
`;

exports[`Image Validation errors when file extension and content do not match up 1`] = `
Array [
Object {
"data": "./files/secretlyPng.jpg",
"errorCode": "FILE_EXTENSION_MISMATCH",
"fieldPath": "icon",
"message": "the file extension should match the content, but the file extension is .jpg while the file content at './files/secretlyPng.jpg' is of type png",
"meta": Object {
"asset": true,
"bareWorkflow": "To change your app's icon, edit or replace the files in \`ios/<PROJECT-NAME>/Assets.xcassets/AppIcon.appiconset\` (we recommend using Xcode), and \`android/app/src/main/res/mipmap-<RESOLUTION>\`. Be sure to follow the guidelines for each platform ([iOS](https://developer.apple.com/design/human-interface-guidelines/ios/icons-and-images/app-icon/), [Android 7.1 and below](https://material.io/design/iconography/#icon-treatments), and [Android 8+](https://developer.android.com/guide/practices/ui_guidelines/icon_design_adaptive)) and to provide your new icon in each existing size.",
"contentTypeHuman": ".png image",
"contentTypePattern": "^image/png$",
"square": true,
},
"name": "ValidationError",
},
]
`;

exports[`Individual Unit Tests Error when data has an additional property 1`] = `
Array [
Object {
"data": Object {
"extraProperty": "extra",
},
"errorCode": "SCHEMA_ADDITIONAL_PROPERTY",
"fieldPath": "",
"message": "should NOT have additional property 'extraProperty'",
"meta": undefined,
"name": "ValidationError",
},
]
`;

exports[`Individual Unit Tests Error when missing Required Property 1`] = `
Array [
Object {
"data": Object {
"noName": "",
},
"errorCode": "SCHEMA_MISSING_REQUIRED_PROPERTY",
"fieldPath": "",
"message": "is missing required property 'name'",
"meta": undefined,
"name": "ValidationError",
},
]
`;

exports[`Manual Validation Individual Unit Tests Local icon dimensions wrong 1`] = `
Array [
Object {
"data": "./files/check.png",
"errorCode": "INVALID_DIMENSIONS",
"fieldPath": "icon",
"message": "'icon' should have dimensions 400x401, but the file at './files/check.png' has dimensions 512x512",
"meta": Object {
"asset": true,
"contentTypePattern": "^image/png$",
"dimensions": Object {
"height": 401,
"width": 400,
},
},
"name": "ValidationError",
},
]
`;
10 changes: 10 additions & 0 deletions packages/schemer/__tests__/files/invalidAppIcon.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "test app",
"slug": "asdfasdf",
"sdkVersion": "17.0.0",
"orientation": "portrait",
"android": {
"package": "com.yourcompany.yourappname"
},
"icon": "./unknown/path.png"
}
Binary file added packages/schemer/__tests__/files/secretlyPng.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added packages/schemer/__tests__/files/webp.webp
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ const S = new Schemer(schema, { rootDir: './__tests__' });

describe('Remote', () => {
it('Icon', async () => {
await expect(
S.validateIcon(
expect(
await S.validateIcon(
'https://upload.wikimedia.org/wikipedia/commons/0/0f/Icon_Pinguin_2_512x512.png'
)
).resolves;
).toEqual(undefined);
});

it('Remote icon dimensions correct', async () => {
Expand All @@ -20,15 +20,20 @@ describe('Remote', () => {
},
},
});
await expect(S.validateIcon('https://httpbin.org/image/png')).resolves;
expect(await S.validateIcon('https://httpbin.org/image/png')).toEqual(undefined);
});

it('Remote icon dimensions wrong', async () => {
let didError = false;
const S = new Schemer(
{
properties: {
icon: {
meta: { asset: true, dimensions: { width: 101, height: 100 } },
meta: {
asset: true,
dimensions: { width: 101, height: 100 },
contentTypePattern: '^image/png$',
},
},
},
},
Expand All @@ -37,8 +42,17 @@ describe('Remote', () => {
try {
await S.validateIcon('https://httpbin.org/image/png');
} catch (e) {
didError = true;
expect(e).toBeTruthy();
expect(e.errors.length).toBe(1);
expect(
e.errors.map(validationError => {
const { stack, ...rest } = validationError;
return rest;
})
).toMatchSnapshot();
}

expect(didError).toBe(true);
});
});
Loading

0 comments on commit e1f5432

Please sign in to comment.