Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[expo-sqlite] Fix Web support #8518

Merged
merged 9 commits into from May 29, 2020
4 changes: 2 additions & 2 deletions apps/test-suite/TestModules.js
Expand Up @@ -69,7 +69,8 @@ export function getTestModules() {
require('./tests/HTML'),
require('./tests/FirebaseCore'),
require('./tests/FirebaseAnalytics'),
require('./tests/FirebaseRecaptcha')
require('./tests/FirebaseRecaptcha'),
optionalRequire(() => require('./tests/SQLite'))
);

if (Platform.OS === 'android') {
Expand Down Expand Up @@ -117,7 +118,6 @@ export function getTestModules() {
optionalRequire(() => require('./tests/Network')),
optionalRequire(() => require('./tests/SecureStore')),
optionalRequire(() => require('./tests/Segment')),
optionalRequire(() => require('./tests/SQLite')),
optionalRequire(() => require('./tests/Speech')),
optionalRequire(() => require('./tests/Recording')),
optionalRequire(() => require('./tests/ScreenOrientation')),
Expand Down
179 changes: 95 additions & 84 deletions apps/test-suite/tests/SQLite.js
@@ -1,5 +1,6 @@
'use strict';

import { Platform } from '@unimodules/core';
import { Asset } from 'expo-asset';
import * as FS from 'expo-file-system';
import * as SQLite from 'expo-sqlite';
Expand Down Expand Up @@ -48,7 +49,7 @@ export function test(t) {
[],
(tx, results) => {
t.expect(results.rows.length).toEqual(3);
t.expect(results.rows._array[0].j).toBeCloseTo(23.4);
t.expect(results.rows.item(0).j).toBeCloseTo(23.4);
},
onError
);
Expand All @@ -58,41 +59,44 @@ export function test(t) {
);
});

const { exists } = await FS.getInfoAsync(`${FS.documentDirectory}SQLite/test.db`);
t.expect(exists).toBeTruthy();
if (Platform.OS !== 'web') {
const { exists } = await FS.getInfoAsync(`${FS.documentDirectory}SQLite/test.db`);
t.expect(exists).toBeTruthy();
}
});

t.it(
'should work with a downloaded .db file',
async () => {
await FS.downloadAsync(
Asset.fromModule(require('../assets/asset-db.db')).uri,
`${FS.documentDirectory}SQLite/downloaded.db`
);

const db = SQLite.openDatabase('downloaded.db');
await new Promise((resolve, reject) => {
db.transaction(
tx => {
const nop = () => {};
const onError = (tx, error) => reject(error);
tx.executeSql(
'SELECT * FROM Users',
[],
(tx, results) => {
t.expect(results.rows.length).toEqual(3);
t.expect(results.rows._array[0].j).toBeCloseTo(23.4);
},
onError
);
},
reject,
resolve
if (Platform.OS !== 'web') {
t.it(
'should work with a downloaded .db file',
async () => {
await FS.downloadAsync(
Asset.fromModule(require('../assets/asset-db.db')).uri,
`${FS.documentDirectory}SQLite/downloaded.db`
);
});
},
30000
);

const db = SQLite.openDatabase('downloaded.db');
await new Promise((resolve, reject) => {
db.transaction(
tx => {
const onError = (tx, error) => reject(error);
tx.executeSql(
'SELECT * FROM Users',
[],
(tx, results) => {
t.expect(results.rows.length).toEqual(3);
t.expect(results.rows._array[0].j).toBeCloseTo(23.4);
},
onError
);
},
reject,
resolve
);
});
},
30000
);
}

t.it('should be able to recreate db from scratch by deleting file', async () => {
{
Expand Down Expand Up @@ -132,12 +136,12 @@ export function test(t) {
});
}

{
if (Platform.OS !== 'web') {
const { exists } = await FS.getInfoAsync(`${FS.documentDirectory}SQLite/test.db`);
t.expect(exists).toBeTruthy();
}

{
if (Platform.OS !== 'web') {
await FS.deleteAsync(`${FS.documentDirectory}SQLite/test.db`);
const { exists } = await FS.getInfoAsync(`${FS.documentDirectory}SQLite/test.db`);
t.expect(exists).toBeFalsy();
Expand Down Expand Up @@ -211,10 +215,10 @@ export function test(t) {
'SELECT * FROM Nulling',
[],
(tx, results) => {
t.expect(results.rows._array[0].x).toBeNull();
t.expect(results.rows._array[0].y).toBeNull();
t.expect(results.rows._array[1].x).toBeNull();
t.expect(results.rows._array[1].y).toBeNull();
t.expect(results.rows.item(0).x).toBeNull();
t.expect(results.rows.item(0).y).toBeNull();
t.expect(results.rows.item(1).x).toBeNull();
t.expect(results.rows.item(1).y).toBeNull();
},
onError
);
Expand All @@ -224,54 +228,61 @@ export function test(t) {
);
});

const { exists } = await FS.getInfoAsync(`${FS.documentDirectory}SQLite/test.db`);
t.expect(exists).toBeTruthy();
if (Platform.OS !== 'web') {
const { exists } = await FS.getInfoAsync(`${FS.documentDirectory}SQLite/test.db`);
t.expect(exists).toBeTruthy();
}
});

t.it('should support PRAGMA statements', async () => {
const db = SQLite.openDatabase('test.db');
await new Promise((resolve, reject) => {
db.transaction(
tx => {
const nop = () => {};
const onError = (tx, error) => reject(error);
// Do not try to test PRAGMA statements support in web
// as it is expected to not be working.
// See https://stackoverflow.com/a/10298712
if (Platform.OS !== 'web') {
t.it('should support PRAGMA statements', async () => {
const db = SQLite.openDatabase('test.db');
await new Promise((resolve, reject) => {
db.transaction(
tx => {
const nop = () => {};
const onError = (tx, error) => reject(error);

tx.executeSql('DROP TABLE IF EXISTS SomeTable;', [], nop, onError);
tx.executeSql(
'CREATE TABLE IF NOT EXISTS SomeTable (id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(64));',
[],
nop,
onError
);
// a result-returning pragma
tx.executeSql(
'PRAGMA table_info(SomeTable);',
[],
(tx, results) => {
t.expect(results.rows.length).toEqual(2);
t.expect(results.rows._array[0].name).toEqual('id');
t.expect(results.rows._array[1].name).toEqual('name');
},
onError
);
// a no-result pragma
tx.executeSql('PRAGMA case_sensitive_like = true;', [], nop, onError);
// a setter/getter pragma
tx.executeSql('PRAGMA user_version = 123;', [], nop, onError);
tx.executeSql(
'PRAGMA user_version;',
[],
(tx, results) => {
t.expect(results.rows.length).toEqual(1);
t.expect(results.rows._array[0].user_version).toEqual(123);
},
onError
);
},
reject,
resolve
);
tx.executeSql('DROP TABLE IF EXISTS SomeTable;', [], nop, onError);
tx.executeSql(
'CREATE TABLE IF NOT EXISTS SomeTable (id INTEGER PRIMARY KEY NOT NULL, name VARCHAR(64));',
[],
nop,
onError
);
// a result-returning pragma
tx.executeSql(
'PRAGMA table_info(SomeTable);',
[],
(tx, results) => {
t.expect(results.rows.length).toEqual(2);
t.expect(results.rows.item(0).name).toEqual('id');
t.expect(results.rows.item(1).name).toEqual('name');
},
onError
);
// a no-result pragma
tx.executeSql('PRAGMA case_sensitive_like = true;', [], nop, onError);
// a setter/getter pragma
tx.executeSql('PRAGMA user_version = 123;', [], nop, onError);
tx.executeSql(
'PRAGMA user_version;',
[],
(tx, results) => {
t.expect(results.rows.length).toEqual(1);
t.expect(results.rows.item(0).user_version).toEqual(123);
},
onError
);
},
reject,
resolve
);
});
});
});
}
});
}
2 changes: 2 additions & 0 deletions packages/expo-sqlite/CHANGELOG.md
Expand Up @@ -8,6 +8,8 @@

### 🐛 Bug fixes

- Fixed support for using `expo-sqlite` on Web ([#8518](https://github.com/expo/expo/pull/8518) by [@sjchmiela](https://github.com/sjchmiela))

## 8.2.0 — 2020-05-27

*This version does not introduce any user-facing changes.*
3 changes: 3 additions & 0 deletions packages/expo-sqlite/build/SQLite.types.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion packages/expo-sqlite/build/SQLite.types.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/expo-sqlite/build/SQLite.types.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 2 additions & 5 deletions packages/expo-sqlite/build/SQLite.web.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 8 additions & 2 deletions packages/expo-sqlite/build/SQLite.web.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/expo-sqlite/build/SQLite.web.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion packages/expo-sqlite/package.json
Expand Up @@ -44,7 +44,6 @@
},
"dependencies": {
"@expo/websql": "^1.0.1",
"@types/websql": "^0.0.27",
"lodash": "^4.17.15"
},
"devDependencies": {
Expand Down
17 changes: 16 additions & 1 deletion packages/expo-sqlite/src/SQLite.types.ts
@@ -1,4 +1,19 @@
// Definitions by: TeamworkGuy2 <https://github.com/TeamworkGuy2>
// Definitions copied from `@types/websql` as we want
// to expose a custom version of the API that:
// - uses primitive `string` instead of `String`
// - excludes some methods that are not exposed by our API.
//
// Original definitions by: TeamworkGuy2 <https://github.com/TeamworkGuy2>

export interface Window {
openDatabase?: (
name: string,
version: string,
displayName: string,
estimatedSize: number,
creationCallback?: DatabaseCallback
) => Database;
}

export interface DatabaseCallback {
(database: Database): void;
Expand Down
19 changes: 17 additions & 2 deletions packages/expo-sqlite/src/SQLite.web.ts
@@ -1,2 +1,17 @@
export const { openDatabase } = global;
export default { openDatabase };
import { UnavailabilityError } from '@unimodules/core';

import { Window, DatabaseCallback } from './SQLite.types';

export function openDatabase(
name: string,
version: string = '1.0',
description: string = name,
size: number = 1,
callback?: DatabaseCallback
) {
const typedWindow: Window = window as Window;
if ('openDatabase' in typedWindow && typedWindow.openDatabase) {
return typedWindow.openDatabase(name, version, description, size, callback);
}
throw new UnavailabilityError('window', 'openDatabase');
}
1 change: 0 additions & 1 deletion packages/expo-sqlite/src/ts-declarations/global.d.ts

This file was deleted.

7 changes: 1 addition & 6 deletions packages/expo-sqlite/src/ts-declarations/process.d.ts
@@ -1,6 +1 @@
declare const process: {
env: {
NODE_ENV: string;
};
[key: string]: any;
};
declare const process: Record<string, any>;
5 changes: 0 additions & 5 deletions yarn.lock
Expand Up @@ -2949,11 +2949,6 @@
"@types/webpack-sources" "*"
source-map "^0.6.0"

"@types/websql@^0.0.27":
version "0.0.27"
resolved "https://registry.yarnpkg.com/@types/websql/-/websql-0.0.27.tgz#621a666a7f02018e7cbb4abab956a25736c27d71"
integrity sha1-Yhpman8CAY58u0q6uVaiVzbCfXE=

"@types/yargs-parser@*":
version "15.0.0"
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d"
Expand Down