增加晶全app静态页面

This commit is contained in:
微微一笑
2025-07-05 14:49:26 +08:00
parent 194035cf79
commit aede64dacd
2323 changed files with 524101 additions and 0 deletions

View File

@ -0,0 +1,45 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="user-scalable=no" />
<title>SQLitePlugin Jasmine Spec Runner</title>
<link rel="shortcut icon" type="image/png" href="lib/jasmine-2.5.2/jasmine_favicon.png">
<link rel="stylesheet" href="lib/jasmine-2.5.2/jasmine.css">
<script src="lib/jasmine-2.5.2/jasmine.js"></script>
<script src="lib/jasmine-2.5.2/jasmine-html.js"></script>
<script src="lib/jasmine-2.5.2/boot.js"></script>
<!-- [Cordova] source file(s): -->
<script src="cordova.js"></script>
<!-- browser startup test: -->
<script src="spec/browser-check-startup.js"></script>
<!-- other spec file(s): -->
<script src="spec/self-test.js"></script>
<script src="spec/sqlite-version-test.js"></script>
<script src="spec/db-tx-string-test.js"></script>
<script src="spec/db-tx-sql-select-value-test.js"></script>
<script src="spec/basic-db-tx-sql-storage-results.js"></script>
<script src="spec/db-sql-operations-test.js"></script>
<script src="spec/sql-batch-test.js"></script>
<script src="spec/db-tx-sql-features-test.js"></script>
<script src="spec/regexp-test.js"></script>
<script src="spec/db-simultaneous-tx-access-test.js"></script>
<script src="spec/db-tx-multiple-update-test.js"></script>
<script src="spec/tx-semantics-test.js"></script>
<script src="spec/db-tx-value-bindings-test.js"></script> <!-- stored [INSERT] value binding tests -->
<script src="spec/db-tx-error-handling-test.js"></script>
<script src="spec/db-tx-error-mapping-test.js"></script>
<script src="spec/ext-tx-blob-test.js"></script> <!-- Blob object value test(s) -->
<script src="spec/db-open-close-delete-test.js"></script> <!-- extended db open/close/delete testing -->
</head>
<body>
</body>
</html> <!-- vim: set expandtab : -->

View File

@ -0,0 +1,130 @@
/**
Starting with version 2.0, this file "boots" Jasmine, performing all of the necessary initialization before executing the loaded environment and all of a project's specs. This file should be loaded after `jasmine.js` and `jasmine_html.js`, but before any project source files or spec files are loaded. Thus this file can also be used to customize Jasmine for a project.
If a project is using Jasmine via the standalone distribution, this file can be customized directly. If a project is using Jasmine via the [Ruby gem][jasmine-gem], this file can be copied into the support directory via `jasmine copy_boot_js`. Other environments (e.g., Python) will have different mechanisms.
The location of `boot.js` can be specified and/or overridden in `jasmine.yml`.
[jasmine-gem]: http://github.com/pivotal/jasmine-gem
*/
(function() {
/**
* ## Require &amp; Instantiate
*
* Require Jasmine's core files. Specifically, this requires and attaches all of Jasmine's code to the `jasmine` reference.
*/
window.jasmine = jasmineRequire.core(jasmineRequire);
/**
* Since this is being run in a browser and the results should populate to an HTML page, require the HTML-specific Jasmine code, injecting the same reference.
*/
jasmineRequire.html(jasmine);
/**
* Create the Jasmine environment. This is used to run all specs in a project.
*/
var env = jasmine.getEnv();
/**
* ## The Global Interface
*
* Build up the functions that will be exposed as the Jasmine public interface. A project can customize, rename or alias any of these functions as desired, provided the implementation remains unchanged.
*/
var jasmineInterface = jasmineRequire.interface(jasmine, env);
/**
* Add all of the Jasmine global/public interface to the global scope, so a project can use the public interface directly. For example, calling `describe` in specs instead of `jasmine.getEnv().describe`.
*/
extend(window, jasmineInterface);
/**
* ## Runner Parameters
*
* More browser specific code - wrap the query string in an object and to allow for getting/setting parameters from the runner user interface.
*/
var queryString = new jasmine.QueryString({
getWindowLocation: function() { return window.location; }
});
var catchingExceptions = queryString.getParam("catch");
env.catchExceptions(typeof catchingExceptions === "undefined" ? true : catchingExceptions);
var throwingExpectationFailures = queryString.getParam("throwFailures");
env.throwOnExpectationFailure(throwingExpectationFailures);
var random = queryString.getParam("random");
env.randomizeTests(random);
var seed = queryString.getParam("seed");
if (seed) {
env.seed(seed);
}
/**
* ## Reporters
* The `HtmlReporter` builds all of the HTML UI for the runner page. This reporter paints the dots, stars, and x's for specs, as well as all spec names and all failures (if any).
*/
var htmlReporter = new jasmine.HtmlReporter({
env: env,
onRaiseExceptionsClick: function() { queryString.navigateWithNewParam("catch", !env.catchingExceptions()); },
onThrowExpectationsClick: function() { queryString.navigateWithNewParam("throwFailures", !env.throwingExpectationFailures()); },
onRandomClick: function() { queryString.navigateWithNewParam("random", !env.randomTests()); },
addToExistingQueryString: function(key, value) { return queryString.fullStringWithNewParam(key, value); },
getContainer: function() { return document.body; },
createElement: function() { return document.createElement.apply(document, arguments); },
createTextNode: function() { return document.createTextNode.apply(document, arguments); },
timer: new jasmine.Timer()
});
/**
* The `jsApiReporter` also receives spec results, and is used by any environment that needs to extract the results from JavaScript.
*/
env.addReporter(jasmineInterface.jsApiReporter);
env.addReporter(htmlReporter);
/**
* Filter which specs will be run by matching the start of the full name against the `spec` query param.
*/
var specFilter = new jasmine.HtmlSpecFilter({
filterString: function() { return queryString.getParam("spec"); }
});
env.specFilter = function(spec) {
return specFilter.matches(spec.getFullName());
};
/**
* Setting up timing functions to be able to be overridden. Certain browsers (Safari, IE 8, phantomjs) require this hack.
*/
window.setTimeout = window.setTimeout;
window.setInterval = window.setInterval;
window.clearTimeout = window.clearTimeout;
window.clearInterval = window.clearInterval;
/**
* ## Execution
*
* Replace the browser window's `onload`, ensure it's called, and then run all of the loaded specs. This includes initializing the `HtmlReporter` instance and then executing the loaded Jasmine environment. All of this will happen after all of the specs are loaded.
*/
var currentWindowOnload = window.onload;
window.onload = function() {
if (currentWindowOnload) {
currentWindowOnload();
}
htmlReporter.initialize();
env.execute();
};
/**
* Helper function for readability above.
*/
function extend(destination, source) {
for (var property in source) destination[property] = source[property];
return destination;
}
}());

View File

@ -0,0 +1,190 @@
/*
Copyright (c) 2008-2016 Pivotal Labs
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
function getJasmineRequireObj() {
if (typeof module !== 'undefined' && module.exports) {
return exports;
} else {
window.jasmineRequire = window.jasmineRequire || {};
return window.jasmineRequire;
}
}
getJasmineRequireObj().console = function(jRequire, j$) {
j$.ConsoleReporter = jRequire.ConsoleReporter();
};
getJasmineRequireObj().ConsoleReporter = function() {
var noopTimer = {
start: function(){},
elapsed: function(){ return 0; }
};
function ConsoleReporter(options) {
var print = options.print,
showColors = options.showColors || false,
onComplete = options.onComplete || function() {},
timer = options.timer || noopTimer,
specCount,
failureCount,
failedSpecs = [],
pendingCount,
ansi = {
green: '\x1B[32m',
red: '\x1B[31m',
yellow: '\x1B[33m',
none: '\x1B[0m'
},
failedSuites = [];
print('ConsoleReporter is deprecated and will be removed in a future version.');
this.jasmineStarted = function() {
specCount = 0;
failureCount = 0;
pendingCount = 0;
print('Started');
printNewline();
timer.start();
};
this.jasmineDone = function() {
printNewline();
for (var i = 0; i < failedSpecs.length; i++) {
specFailureDetails(failedSpecs[i]);
}
if(specCount > 0) {
printNewline();
var specCounts = specCount + ' ' + plural('spec', specCount) + ', ' +
failureCount + ' ' + plural('failure', failureCount);
if (pendingCount) {
specCounts += ', ' + pendingCount + ' pending ' + plural('spec', pendingCount);
}
print(specCounts);
} else {
print('No specs found');
}
printNewline();
var seconds = timer.elapsed() / 1000;
print('Finished in ' + seconds + ' ' + plural('second', seconds));
printNewline();
for(i = 0; i < failedSuites.length; i++) {
suiteFailureDetails(failedSuites[i]);
}
onComplete(failureCount === 0);
};
this.specDone = function(result) {
specCount++;
if (result.status == 'pending') {
pendingCount++;
print(colored('yellow', '*'));
return;
}
if (result.status == 'passed') {
print(colored('green', '.'));
return;
}
if (result.status == 'failed') {
failureCount++;
failedSpecs.push(result);
print(colored('red', 'F'));
}
};
this.suiteDone = function(result) {
if (result.failedExpectations && result.failedExpectations.length > 0) {
failureCount++;
failedSuites.push(result);
}
};
return this;
function printNewline() {
print('\n');
}
function colored(color, str) {
return showColors ? (ansi[color] + str + ansi.none) : str;
}
function plural(str, count) {
return count == 1 ? str : str + 's';
}
function repeat(thing, times) {
var arr = [];
for (var i = 0; i < times; i++) {
arr.push(thing);
}
return arr;
}
function indent(str, spaces) {
var lines = (str || '').split('\n');
var newArr = [];
for (var i = 0; i < lines.length; i++) {
newArr.push(repeat(' ', spaces).join('') + lines[i]);
}
return newArr.join('\n');
}
function specFailureDetails(result) {
printNewline();
print(result.fullName);
for (var i = 0; i < result.failedExpectations.length; i++) {
var failedExpectation = result.failedExpectations[i];
printNewline();
print(indent(failedExpectation.message, 2));
print(indent(failedExpectation.stack, 2));
}
printNewline();
}
function suiteFailureDetails(result) {
for (var i = 0; i < result.failedExpectations.length; i++) {
printNewline();
print(colored('red', 'An error was thrown in an afterAll'));
printNewline();
print(colored('red', 'AfterAll ' + result.failedExpectations[i].message));
}
printNewline();
}
}
return ConsoleReporter;
};

View File

@ -0,0 +1,481 @@
/*
Copyright (c) 2008-2016 Pivotal Labs
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
jasmineRequire.html = function(j$) {
j$.ResultsNode = jasmineRequire.ResultsNode();
j$.HtmlReporter = jasmineRequire.HtmlReporter(j$);
j$.QueryString = jasmineRequire.QueryString();
j$.HtmlSpecFilter = jasmineRequire.HtmlSpecFilter();
};
jasmineRequire.HtmlReporter = function(j$) {
var noopTimer = {
start: function() {},
elapsed: function() { return 0; }
};
function HtmlReporter(options) {
var env = options.env || {},
getContainer = options.getContainer,
createElement = options.createElement,
createTextNode = options.createTextNode,
onRaiseExceptionsClick = options.onRaiseExceptionsClick || function() {},
onThrowExpectationsClick = options.onThrowExpectationsClick || function() {},
onRandomClick = options.onRandomClick || function() {},
addToExistingQueryString = options.addToExistingQueryString || defaultQueryString,
timer = options.timer || noopTimer,
results = [],
specsExecuted = 0,
failureCount = 0,
pendingSpecCount = 0,
htmlReporterMain,
symbols,
failedSuites = [];
this.initialize = function() {
clearPrior();
htmlReporterMain = createDom('div', {className: 'jasmine_html-reporter'},
createDom('div', {className: 'jasmine-banner'},
createDom('a', {className: 'jasmine-title', href: 'http://jasmine.github.io/', target: '_blank'}),
createDom('span', {className: 'jasmine-version'}, j$.version)
),
createDom('ul', {className: 'jasmine-symbol-summary'}),
createDom('div', {className: 'jasmine-alert'}),
createDom('div', {className: 'jasmine-results'},
createDom('div', {className: 'jasmine-failures'})
)
);
getContainer().appendChild(htmlReporterMain);
};
var totalSpecsDefined;
this.jasmineStarted = function(options) {
totalSpecsDefined = options.totalSpecsDefined || 0;
timer.start();
};
var summary = createDom('div', {className: 'jasmine-summary'});
var topResults = new j$.ResultsNode({}, '', null),
currentParent = topResults;
this.suiteStarted = function(result) {
currentParent.addChild(result, 'suite');
currentParent = currentParent.last();
};
this.suiteDone = function(result) {
if (result.status == 'failed') {
failedSuites.push(result);
}
if (currentParent == topResults) {
return;
}
currentParent = currentParent.parent;
};
this.specStarted = function(result) {
currentParent.addChild(result, 'spec');
};
var failures = [];
this.specDone = function(result) {
if(noExpectations(result) && typeof console !== 'undefined' && typeof console.error !== 'undefined') {
console.error('Spec \'' + result.fullName + '\' has no expectations.');
}
if (result.status != 'disabled') {
specsExecuted++;
}
if (!symbols){
symbols = find('.jasmine-symbol-summary');
}
symbols.appendChild(createDom('li', {
className: noExpectations(result) ? 'jasmine-empty' : 'jasmine-' + result.status,
id: 'spec_' + result.id,
title: result.fullName
}
));
if (result.status == 'failed') {
failureCount++;
var failure =
createDom('div', {className: 'jasmine-spec-detail jasmine-failed'},
createDom('div', {className: 'jasmine-description'},
createDom('a', {title: result.fullName, href: specHref(result)}, result.fullName)
),
createDom('div', {className: 'jasmine-messages'})
);
var messages = failure.childNodes[1];
for (var i = 0; i < result.failedExpectations.length; i++) {
var expectation = result.failedExpectations[i];
messages.appendChild(createDom('div', {className: 'jasmine-result-message'}, expectation.message));
messages.appendChild(createDom('div', {className: 'jasmine-stack-trace'}, expectation.stack));
}
failures.push(failure);
}
if (result.status == 'pending') {
pendingSpecCount++;
}
};
this.jasmineDone = function(doneResult) {
var banner = find('.jasmine-banner');
var alert = find('.jasmine-alert');
var order = doneResult && doneResult.order;
alert.appendChild(createDom('span', {className: 'jasmine-duration'}, 'finished in ' + timer.elapsed() / 1000 + 's'));
banner.appendChild(
createDom('div', { className: 'jasmine-run-options' },
createDom('span', { className: 'jasmine-trigger' }, 'Options'),
createDom('div', { className: 'jasmine-payload' },
createDom('div', { className: 'jasmine-exceptions' },
createDom('input', {
className: 'jasmine-raise',
id: 'jasmine-raise-exceptions',
type: 'checkbox'
}),
createDom('label', { className: 'jasmine-label', 'for': 'jasmine-raise-exceptions' }, 'raise exceptions')),
createDom('div', { className: 'jasmine-throw-failures' },
createDom('input', {
className: 'jasmine-throw',
id: 'jasmine-throw-failures',
type: 'checkbox'
}),
createDom('label', { className: 'jasmine-label', 'for': 'jasmine-throw-failures' }, 'stop spec on expectation failure')),
createDom('div', { className: 'jasmine-random-order' },
createDom('input', {
className: 'jasmine-random',
id: 'jasmine-random-order',
type: 'checkbox'
}),
createDom('label', { className: 'jasmine-label', 'for': 'jasmine-random-order' }, 'run tests in random order'))
)
));
var raiseCheckbox = find('#jasmine-raise-exceptions');
raiseCheckbox.checked = !env.catchingExceptions();
raiseCheckbox.onclick = onRaiseExceptionsClick;
var throwCheckbox = find('#jasmine-throw-failures');
throwCheckbox.checked = env.throwingExpectationFailures();
throwCheckbox.onclick = onThrowExpectationsClick;
var randomCheckbox = find('#jasmine-random-order');
randomCheckbox.checked = env.randomTests();
randomCheckbox.onclick = onRandomClick;
var optionsMenu = find('.jasmine-run-options'),
optionsTrigger = optionsMenu.querySelector('.jasmine-trigger'),
optionsPayload = optionsMenu.querySelector('.jasmine-payload'),
isOpen = /\bjasmine-open\b/;
optionsTrigger.onclick = function() {
if (isOpen.test(optionsPayload.className)) {
optionsPayload.className = optionsPayload.className.replace(isOpen, '');
} else {
optionsPayload.className += ' jasmine-open';
}
};
if (specsExecuted < totalSpecsDefined) {
var skippedMessage = 'Ran ' + specsExecuted + ' of ' + totalSpecsDefined + ' specs - run all';
var skippedLink = order && order.random ? '?random=true' : '?';
alert.appendChild(
createDom('span', {className: 'jasmine-bar jasmine-skipped'},
createDom('a', {href: skippedLink, title: 'Run all specs'}, skippedMessage)
)
);
}
var statusBarMessage = '';
var statusBarClassName = 'jasmine-bar ';
if (totalSpecsDefined > 0) {
statusBarMessage += pluralize('spec', specsExecuted) + ', ' + pluralize('failure', failureCount);
if (pendingSpecCount) { statusBarMessage += ', ' + pluralize('pending spec', pendingSpecCount); }
statusBarClassName += (failureCount > 0) ? 'jasmine-failed' : 'jasmine-passed';
} else {
statusBarClassName += 'jasmine-skipped';
statusBarMessage += 'No specs found';
}
var seedBar;
if (order && order.random) {
seedBar = createDom('span', {className: 'jasmine-seed-bar'},
', randomized with seed ',
createDom('a', {title: 'randomized with seed ' + order.seed, href: seedHref(order.seed)}, order.seed)
);
}
alert.appendChild(createDom('span', {className: statusBarClassName}, statusBarMessage, seedBar));
var errorBarClassName = 'jasmine-bar jasmine-errored';
var errorBarMessagePrefix = 'AfterAll ';
for(var i = 0; i < failedSuites.length; i++) {
var failedSuite = failedSuites[i];
for(var j = 0; j < failedSuite.failedExpectations.length; j++) {
alert.appendChild(createDom('span', {className: errorBarClassName}, errorBarMessagePrefix + failedSuite.failedExpectations[j].message));
}
}
var globalFailures = (doneResult && doneResult.failedExpectations) || [];
for(i = 0; i < globalFailures.length; i++) {
var failure = globalFailures[i];
alert.appendChild(createDom('span', {className: errorBarClassName}, errorBarMessagePrefix + failure.message));
}
var results = find('.jasmine-results');
results.appendChild(summary);
summaryList(topResults, summary);
function summaryList(resultsTree, domParent) {
var specListNode;
for (var i = 0; i < resultsTree.children.length; i++) {
var resultNode = resultsTree.children[i];
if (resultNode.type == 'suite') {
var suiteListNode = createDom('ul', {className: 'jasmine-suite', id: 'suite-' + resultNode.result.id},
createDom('li', {className: 'jasmine-suite-detail'},
createDom('a', {href: specHref(resultNode.result)}, resultNode.result.description)
)
);
summaryList(resultNode, suiteListNode);
domParent.appendChild(suiteListNode);
}
if (resultNode.type == 'spec') {
if (domParent.getAttribute('class') != 'jasmine-specs') {
specListNode = createDom('ul', {className: 'jasmine-specs'});
domParent.appendChild(specListNode);
}
var specDescription = resultNode.result.description;
if(noExpectations(resultNode.result)) {
specDescription = 'SPEC HAS NO EXPECTATIONS ' + specDescription;
}
if(resultNode.result.status === 'pending' && resultNode.result.pendingReason !== '') {
specDescription = specDescription + ' PENDING WITH MESSAGE: ' + resultNode.result.pendingReason;
}
specListNode.appendChild(
createDom('li', {
className: 'jasmine-' + resultNode.result.status,
id: 'spec-' + resultNode.result.id
},
createDom('a', {href: specHref(resultNode.result)}, specDescription)
)
);
}
}
}
if (failures.length) {
alert.appendChild(
createDom('span', {className: 'jasmine-menu jasmine-bar jasmine-spec-list'},
createDom('span', {}, 'Spec List | '),
createDom('a', {className: 'jasmine-failures-menu', href: '#'}, 'Failures')));
alert.appendChild(
createDom('span', {className: 'jasmine-menu jasmine-bar jasmine-failure-list'},
createDom('a', {className: 'jasmine-spec-list-menu', href: '#'}, 'Spec List'),
createDom('span', {}, ' | Failures ')));
find('.jasmine-failures-menu').onclick = function() {
setMenuModeTo('jasmine-failure-list');
};
find('.jasmine-spec-list-menu').onclick = function() {
setMenuModeTo('jasmine-spec-list');
};
setMenuModeTo('jasmine-failure-list');
var failureNode = find('.jasmine-failures');
for (i = 0; i < failures.length; i++) {
failureNode.appendChild(failures[i]);
}
}
};
return this;
function find(selector) {
return getContainer().querySelector('.jasmine_html-reporter ' + selector);
}
function clearPrior() {
// return the reporter
var oldReporter = find('');
if(oldReporter) {
getContainer().removeChild(oldReporter);
}
}
function createDom(type, attrs, childrenVarArgs) {
var el = createElement(type);
for (var i = 2; i < arguments.length; i++) {
var child = arguments[i];
if (typeof child === 'string') {
el.appendChild(createTextNode(child));
} else {
if (child) {
el.appendChild(child);
}
}
}
for (var attr in attrs) {
if (attr == 'className') {
el[attr] = attrs[attr];
} else {
el.setAttribute(attr, attrs[attr]);
}
}
return el;
}
function pluralize(singular, count) {
var word = (count == 1 ? singular : singular + 's');
return '' + count + ' ' + word;
}
function specHref(result) {
return addToExistingQueryString('spec', result.fullName);
}
function seedHref(seed) {
return addToExistingQueryString('seed', seed);
}
function defaultQueryString(key, value) {
return '?' + key + '=' + value;
}
function setMenuModeTo(mode) {
htmlReporterMain.setAttribute('class', 'jasmine_html-reporter ' + mode);
}
function noExpectations(result) {
return (result.failedExpectations.length + result.passedExpectations.length) === 0 &&
result.status === 'passed';
}
}
return HtmlReporter;
};
jasmineRequire.HtmlSpecFilter = function() {
function HtmlSpecFilter(options) {
var filterString = options && options.filterString() && options.filterString().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
var filterPattern = new RegExp(filterString);
this.matches = function(specName) {
return filterPattern.test(specName);
};
}
return HtmlSpecFilter;
};
jasmineRequire.ResultsNode = function() {
function ResultsNode(result, type, parent) {
this.result = result;
this.type = type;
this.parent = parent;
this.children = [];
this.addChild = function(result, type) {
this.children.push(new ResultsNode(result, type, this));
};
this.last = function() {
return this.children[this.children.length - 1];
};
}
return ResultsNode;
};
jasmineRequire.QueryString = function() {
function QueryString(options) {
this.navigateWithNewParam = function(key, value) {
options.getWindowLocation().search = this.fullStringWithNewParam(key, value);
};
this.fullStringWithNewParam = function(key, value) {
var paramMap = queryStringToParamMap();
paramMap[key] = value;
return toQueryString(paramMap);
};
this.getParam = function(key) {
return queryStringToParamMap()[key];
};
return this;
function toQueryString(paramMap) {
var qStrPairs = [];
for (var prop in paramMap) {
qStrPairs.push(encodeURIComponent(prop) + '=' + encodeURIComponent(paramMap[prop]));
}
return '?' + qStrPairs.join('&');
}
function queryStringToParamMap() {
var paramStr = options.getWindowLocation().search.substring(1),
params = [],
paramMap = {};
if (paramStr.length > 0) {
params = paramStr.split('&');
for (var i = 0; i < params.length; i++) {
var p = params[i].split('=');
var value = decodeURIComponent(p[1]);
if (value === 'true' || value === 'false') {
value = JSON.parse(value);
}
paramMap[decodeURIComponent(p[0])] = value;
}
}
return paramMap;
}
}
return QueryString;
};

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,35 @@
/* 'use strict'; */
var MYTIMEOUT = 12000;
var isWindows = /MSAppHost/.test(navigator.userAgent);
var isAndroid = !isWindows && /Android/.test(navigator.userAgent);
var isFirefox = /Firefox/.test(navigator.userAgent);
var isWebKitBrowser = !isWindows && !isAndroid && /Safari/.test(navigator.userAgent);
var isBrowser = isWebKitBrowser || isFirefox;
var isEdgeBrowser = isBrowser && (/Edge/.test(navigator.userAgent));
var isChromeBrowser = isBrowser && !isEdgeBrowser && (/Chrome/.test(navigator.userAgent));
var isMac = !isBrowser && /Macintosh/.test(navigator.userAgent);
var isAppleMobileOS = /iPhone/.test(navigator.userAgent) ||
/iPad/.test(navigator.userAgent) || /iPod/.test(navigator.userAgent);
var hasMobileWKWebView = isAppleMobileOS && !!window.webkit && !!window.webkit.messageHandlers;
window.hasBrowser = true;
window.hasWebKitWebSQL = isAndroid || isChromeBrowser;
describe('Check startup for navigator.userAgent: ' + navigator.userAgent, function() {
it('receives deviceready event', function(done) {
expect(true).toBe(true);
document.addEventListener("deviceready", function() {
done();
});
}, MYTIMEOUT);
it('has openDatabase', function() {
if (window.hasWebKitWebSQL) expect(window.openDatabase).toBeDefined();
expect(window.sqlitePlugin).toBeDefined();
expect(window.sqlitePlugin.openDatabase).toBeDefined();
});
});
/* vim: set expandtab : */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,430 @@
/* 'use strict'; */
var MYTIMEOUT = 12000;
// NOTE: DEFAULT_SIZE wanted depends on type of browser
var isWindows = /MSAppHost/.test(navigator.userAgent);
var isAndroid = !isWindows && /Android/.test(navigator.userAgent);
var isFirefox = /Firefox/.test(navigator.userAgent);
var isWebKitBrowser = !isWindows && !isAndroid && /Safari/.test(navigator.userAgent);
var isBrowser = isWebKitBrowser || isFirefox;
var isEdgeBrowser = isBrowser && (/Edge/.test(navigator.userAgent));
var isChromeBrowser = isBrowser && !isEdgeBrowser && (/Chrome/.test(navigator.userAgent));
var isSafariBrowser = isWebKitBrowser && !isEdgeBrowser && !isChromeBrowser;
// should avoid popups (Safari seems to count 2x)
var DEFAULT_SIZE = isSafariBrowser ? 2000000 : 5000000;
// FUTURE TBD: 50MB should be OK on Chrome and some other test browsers.
// NOTE: While in certain version branches there is no difference between
// the default Android implementation and implementation #2,
// this test script will also apply the androidLockWorkaround: 1 option
// in case of implementation #2.
var scenarioList = [
isAndroid ? 'Plugin-implementation-default' : 'Plugin',
'HTML5',
'Plugin-implementation-2'
];
var scenarioCount = (!!window.hasWebKitWebSQL) ? (isAndroid ? 3 : 2) : 1;
function logSuccess(message) { console.log('OK - ' + message); }
function logError(message) { console.log('FAILED - ' + message); }
var mytests = function() {
for (var i=0; i<scenarioCount; ++i) {
describe(scenarioList[i] + ': simultaneous tx access test(s)', function() {
var scenarioName = scenarioList[i];
var suiteName = scenarioName + ': ';
var isWebSql = (i === 1);
var isImpl2 = (i === 2);
// NOTE: MUST be defined in function scope, NOT outer scope:
var openDatabase = function(name, ignored1, ignored2, ignored3) {
if (isImpl2) {
return window.sqlitePlugin.openDatabase({
// prevent reuse of database from default db implementation:
name: 'i2-'+name,
// explicit database location:
location: 'default',
androidDatabaseImplementation: 2,
androidLockWorkaround: 1
});
}
if (isWebSql) {
return window.openDatabase(name, '1.0', 'Test', DEFAULT_SIZE);
} else {
// explicit database location:
return window.sqlitePlugin.openDatabase({name: name, location: 'default'});
}
}
it(suiteName + ' open same db twice with string test', function (done) {
var dbName = 'open-same-db-twice-string-test.db';
var db1 = openDatabase(dbName, "1.0", "Demo", DEFAULT_SIZE);
var db2 = openDatabase(dbName, "1.0", "Demo", DEFAULT_SIZE);
expect(db1).toBeTruthy(); // valid db1 database handle object
expect(db2).toBeTruthy(); // valid db2 database handle object
// Replacement for QUnit stop()/start() functions:
var checkCount = 0;
var expectedCheckCount = 2;
db1.readTransaction(function(tx) {
expect(tx).toBeTruthy(); // valid db1 tx object
tx.executeSql("select upper('first') as uppertext", [], function(tx, result) {
expect(result).toBeTruthy(); // valid db1 read tx result object
expect(result.rows.item(0).uppertext).toBe('FIRST'); // check db1 read tx result
if (++checkCount === expectedCheckCount) done();
}, function(error) {
// ERROR NOT EXPECTED here:
logError(error);
expect(error.message).toBe('--');
done.fail();
});
}, function(error) {
// ERROR NOT EXPECTED here:
logError(error);
expect(error.message).toBe('--');
done.fail();
});
db2.readTransaction(function(tx) {
expect(tx).toBeTruthy(); // valid db2 tx object
tx.executeSql("select upper('second') as uppertext", [], function(tx, result) {
expect(result).toBeTruthy(); // valid db2 read tx result object
expect(result.rows.item(0).uppertext).toBe('SECOND'); // check db2 read tx result
if (++checkCount === expectedCheckCount) done();
}, function(error) {
// ERROR NOT EXPECTED here:
logError(error);
expect(error.message).toBe('--');
done.fail();
});
}, function(error) {
// ERROR NOT EXPECTED here:
logError(error);
expect(error.message).toBe('--');
done.fail();
});
});
it(suiteName + ' test simultaneous transactions (same db handle)', function (done) {
var db = openDatabase("Database-Simultaneous-Tx", "1.0", "Demo", DEFAULT_SIZE);
var numDone = 0;
function checkDone() {
if (++numDone == 2) {
done();
}
}
db.transaction(function(tx) {
tx.executeSql('DROP TABLE IF EXISTS test', [], function () {
tx.executeSql('CREATE TABLE test (name);');
});
}, function(error) {
// ERROR NOT EXPECTED here:
logError(error);
expect(error.message).toBe('--');
done.fail();
}, function () {
db.transaction(function(tx) {
tx.executeSql('INSERT INTO test VALUES ("foo")', [], function () {
tx.executeSql('SELECT * FROM test', [], function (tx, res) {
expect(res.rows.length).toBe(1); // only one row
expect(res.rows.item(0).name).toBe('foo');
tx.executeSql('SELECT * FROM bogustable'); // force rollback
});
});
}, function(error) {
// EXPECTED RESULT - expected error:
expect(error).toBeDefined();
expect(error.message).toBeDefined();
checkDone();
}, function () {
// NOT EXPECTED - should have rolled back:
logError('should have rolled back');
expect(error.message).toBe('--');
done.fail();
});
db.transaction(function(tx) {
tx.executeSql('INSERT INTO test VALUES ("bar")', [], function () {
tx.executeSql('SELECT * FROM test', [], function (tx, res) {
expect(res.rows.length).toBe(1); // only one row
expect(res.rows.item(0).name).toBe('bar');
tx.executeSql('SELECT * FROM bogustable'); // force rollback
});
});
}, function(error) {
// EXPECTED RESULT - expected error:
expect(error).toBeDefined();
expect(error.message).toBeDefined();
checkDone();
}, function () {
// NOT EXPECTED - should have rolled back:
logError('should have rolled back');
done.fail();
});
});
});
it(suiteName + ' test simultaneous transactions, different db handles (same db)', function (done) {
var dbName = "Database-Simultaneous-Tx-Diff-DB-handles";
var db = openDatabase(dbName, "1.0", "Demo", DEFAULT_SIZE);
var numDone = 0;
function checkDone() {
if (++numDone == 2) {
done();
}
}
db.transaction(function(tx) {
tx.executeSql('DROP TABLE IF EXISTS test', [], function () {
tx.executeSql('CREATE TABLE test (name);');
});
}, function(error) {
// ERROR NOT EXPECTED here:
logError(error);
expect(error.message).toBe('--');
done.fail();
}, function () {
var db1 = openDatabase(dbName, "1.0", "Demo", DEFAULT_SIZE);
db1.transaction(function(tx) {
tx.executeSql('INSERT INTO test VALUES ("foo")', [], function () {
tx.executeSql('SELECT * FROM test', [], function (tx, res) {
expect(res.rows.length).toBe(1); // only one row
expect(res.rows.item(0).name).toBe('foo');
tx.executeSql('SELECT * FROM bogustable'); // force rollback
});
});
}, function(error) {
// EXPECTED RESULT - expected error:
expect(error).toBeDefined();
expect(error.message).toBeDefined();
checkDone();
}, function () {
// NOT EXPECTED - should have rolled back:
logError('should have rolled back');
done.fail();
});
var db2 = openDatabase(dbName, "1.0", "Demo", DEFAULT_SIZE);
db2.transaction(function(tx) {
tx.executeSql('INSERT INTO test VALUES ("bar")', [], function () {
tx.executeSql('SELECT * FROM test', [], function (tx, res) {
expect(res.rows.length).toBe(1); // only one row
expect(res.rows.item(0).name).toBe('bar');
tx.executeSql('SELECT * FROM bogustable'); // force rollback
});
});
}, function(error) {
// EXPECTED RESULT - expected error:
expect(error).toBeDefined();
expect(error.message).toBeDefined();
checkDone();
}, function () {
// NOT EXPECTED - should have rolled back:
logError('should have rolled back');
done.fail();
});
});
});
it(suiteName + ' can open two databases at the same time', function (done) {
// create databases and tables
var db1 = openDatabase("DB1", "1.0", "Demo", DEFAULT_SIZE);
db1.transaction(function (tx1) {
tx1.executeSql('CREATE TABLE IF NOT EXISTS test1 (x int)');
});
var db2 = openDatabase("DB2", "1.0", "Demo", DEFAULT_SIZE);
db2.transaction(function (tx2) {
tx2.executeSql('CREATE TABLE IF NOT EXISTS test2 (x int)');
});
// two databases that perform two queries and one commit each, then repeat
// Quick replacement for QUnit stop()/start() functions:
var checkCount = 0;
var expectedCheckCount = 12;
// create overlapping transactions
db1.transaction(function (tx1) {
db2.transaction(function (tx2) {
tx2.executeSql('INSERT INTO test2 VALUES (2)', [], function (tx, result) {
logSuccess('inserted into second database');
++checkCount;
});
tx2.executeSql('SELECT * from test2', [], function (tx, result) {
expect(result.rows.item(0).x).toBe(2); // selected from second database
++checkCount;
});
}, function (error) {
// ERROR NOT EXPECTED here:
logError('transaction 2 failed ' + error);
expect(error.message).toBe('--');
done.fail();
}, function () {
logSuccess('transaction 2 committed');
++checkCount;
});
tx1.executeSql('INSERT INTO test1 VALUES (1)', [], function (tx, result) {
logSuccess('inserted into first database');
++checkCount;
});
tx1.executeSql('SELECT * from test1', [], function (tx, result) {
expect(result.rows.item(0).x).toBe(1); // selected from first database
++checkCount;
});
}, function (error) {
// ERROR NOT EXPECTED here:
logError('transaction 1 failed ' + error);
expect(error.message).toBe('--');
done.fail();
}, function () {
logSuccess('transaction 1 committed');
if (++checkCount === expectedCheckCount) done();
});
// now that the databases are truly open, do it again!
// - should wait for first db1.transaction() call to finish
// - must check for - checkCount === expectedCheckCount in both
// db1.transaction() callback & db2.transaction() callback
// since it is not certain which will finish first or last.
db1.transaction(function (tx1) {
db2.transaction(function (tx2) {
tx2.executeSql('INSERT INTO test2 VALUES (2)', [], function (tx, result) {
logSuccess('inserted into second database');
++checkCount;
});
tx2.executeSql('SELECT * from test2', [], function (tx, result) {
expect(result.rows.item(0).x).toBe(2); // selected from second database
++checkCount;
});
}, function (error) {
// ERROR NOT EXPECTED here:
logError('transaction 2 failed ' + error);
expect(error.message).toBe('--');
done.fail();
}, function () {
logSuccess('transaction 2 committed');
if (++checkCount === expectedCheckCount) done();
});
tx1.executeSql('INSERT INTO test1 VALUES (1)', [], function (tx, result) {
logSuccess('inserted into first database');
++checkCount;
});
tx1.executeSql('SELECT * from test1', [], function (tx, result) {
expect(result.rows.item(0).x).toBe(1); // selected from first database
++checkCount;
});
}, function (error) {
// ERROR NOT EXPECTED here:
logError('transaction 1 failed ' + error);
expect(error.message).toBe('--');
done.fail();
}, function () {
logSuccess('transaction 1 committed');
if (++checkCount === expectedCheckCount) done();
});
});
it(suiteName + ' same database file with separate writer/reader db handles', function (done) {
var dbname = 'writer-reader-test.db';
var dbw = openDatabase(dbname, "1.0", "Demo", DEFAULT_SIZE);
var dbr = openDatabase(dbname, "1.0", "Demo", DEFAULT_SIZE);
dbw.transaction(function (tx) {
tx.executeSql('DROP TABLE IF EXISTS tt');
tx.executeSql('CREATE TABLE IF NOT EXISTS tt (test_data)');
tx.executeSql('INSERT INTO tt VALUES (?)', ['My-test-data']);
}, function(error) {
// ERROR NOT EXPECTED here:
logError(error.message);
expect(error.message).toBe('--');
done.fail();
}, function() {
dbr.readTransaction(function (tx) {
tx.executeSql('SELECT test_data from tt', [], function (tx, result) {
expect(result.rows.item(0).test_data).toBe('My-test-data'); // read data from reader handle
done();
});
}, function(error) {
// ERROR NOT EXPECTED here:
logError(error.message);
expect(error.message).toBe('--');
done.fail();
});
});
});
it(suiteName + ' same database file with multiple writer db handles', function (done) {
var dbname = 'multi-writer-test.db';
var dbw1 = openDatabase(dbname, "1.0", "Demo", DEFAULT_SIZE);
var dbw2 = openDatabase(dbname, "1.0", "Demo", DEFAULT_SIZE);
var dbr = openDatabase(dbname, "1.0", "Demo", DEFAULT_SIZE);
dbw1.transaction(function (tx) {
tx.executeSql('DROP TABLE IF EXISTS tt');
tx.executeSql('CREATE TABLE IF NOT EXISTS tt (test_data)');
}, function(error) {
// ERROR NOT EXPECTED here:
logError(error.message);
expect(error.message).toBe('--');
done.fail();
}, function() {
dbw2.transaction(function (tx) {
tx.executeSql('INSERT INTO tt VALUES (?)', ['My-test-data']);
}, function(error) {
// ERROR NOT EXPECTED here:
logError(error.message);
expect(error.message).toBe('--');
done.fail();
}, function() {
dbr.readTransaction(function (tx) {
tx.executeSql('SELECT test_data from tt', [], function (tx, result) {
expect(result.rows.item(0).test_data).toBe('My-test-data'); // read data from reader handle
done();
});
}, function(error) {
// ERROR NOT EXPECTED here:
logError(error.message);
expect(error.message).toBe('--');
done.fail();
});
});
});
});
});
}
}
if (window.hasBrowser) mytests();
else exports.defineAutoTests = mytests;
/* vim: set expandtab : */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,929 @@
/* 'use strict'; */
var MYTIMEOUT = 12000;
// NOTE: DEFAULT_SIZE wanted depends on type of browser
var isWindows = /MSAppHost/.test(navigator.userAgent);
var isAndroid = !isWindows && /Android/.test(navigator.userAgent);
var isFirefox = /Firefox/.test(navigator.userAgent);
var isWebKitBrowser = !isWindows && !isAndroid && /Safari/.test(navigator.userAgent);
var isBrowser = isWebKitBrowser || isFirefox;
var isEdgeBrowser = isBrowser && (/Edge/.test(navigator.userAgent));
var isChromeBrowser = isBrowser && !isEdgeBrowser && (/Chrome/.test(navigator.userAgent));
var isSafariBrowser = isWebKitBrowser && !isEdgeBrowser && !isChromeBrowser;
// detect iOS platform:
var isAppleMobileOS =
(/iPhone/.test(navigator.userAgent)
|| /iPad/.test(navigator.userAgent)
|| /iPod/.test(navigator.userAgent));
// should avoid popups (Safari seems to count 2x)
var DEFAULT_SIZE = isSafariBrowser ? 2000000 : 5000000;
// FUTURE TBD: 50MB should be OK on Chrome and some other test browsers.
// NOTE: While in certain version branches there is no difference between
// the default Android implementation and implementation #2,
// this test script will also apply the androidLockWorkaround: 1 option
// in case of implementation #2.
var scenarioList = [
isAndroid ? 'Plugin-implementation-default' : 'Plugin',
'HTML5',
'Plugin-implementation-2'
];
var scenarioCount = (!!window.hasWebKitWebSQL) ? (isAndroid ? 3 : 2) : 1;
var mytests = function() {
for (var i=0; i<scenarioCount; ++i) {
describe(scenarioList[i] + ': db tx error mapping test(s) [TBD INCORRECT & INCONSISTENT error message on Windows - missing actual error info ref: litehelpers/Cordova-sqlite-storage#539]', function() {
var scenarioName = scenarioList[i];
var suiteName = scenarioName + ': ';
var isWebSql = (i === 1);
var isImpl2 = (i === 2);
// XXX TBD WORKAROUND SOLUTION for (WebKit) Web SQL on Safari browser (TEST DB NAME IGNORED):
var recycleWebDatabase = null;
// NOTE: MUST be defined in function scope, NOT outer scope:
var openDatabase = function(name, ignored1, ignored2, ignored3) {
if (isWebSql && isSafariBrowser && !!recycleWebDatabase)
return recycleWebDatabase;
if (isImpl2) {
return window.sqlitePlugin.openDatabase({
// prevent reuse of database from default db implementation:
name: 'i2-'+name,
androidDatabaseImplementation: 2,
androidLockWorkaround: 1,
location: 1
});
}
if (isWebSql) {
return recycleWebDatabase =
window.openDatabase(name, '1.0', 'Test', DEFAULT_SIZE);
} else {
return window.sqlitePlugin.openDatabase({name: name, location: 0});
}
}
// ERROR MAPPING ISSUES/DEVIATIONS:
//
// - In case an executeSql error handler returns true (WebKit) Web SQL indicates
// error code 0 (SQLError.UNKNOWN_ERR) in the transaction error callback
// - In certain database error cases (WebKit) Web SQL and plugin on Android
// (no androidDatabaseImplementation: 2 setting) & iOS report SQLError code 5
// (SQLError.SYNTAX_ERR) wile it should be 1 (SQLError.DATABASE_ERR)
// ("not covered by any other error code") ref:
// https://www.w3.org/TR/webdatabase/#dom-sqlerror-code-1
// - Android plugin with androidDatabaseImplementation: 2 setting indicates SQLError code 0
// (SQLError.UNKNOWN_ERR) in cases other than a syntax error or constraint violation
// - Windows plugin always reports error code 0 (SQLError.UNKNOWN_ERR) and
// INCONSISTENT messages (missing actual error info)
// OTHER ERROR MAPPING NOTES:
//
// - (WebKit) Web SQL apparently includes 'prepare statement error' vs
// 'execute statement error' info along with the sqlite error code
// - Default Android implementation (Android-sqlite-connector) includes
// sqlite3_prepare_v2 vs sqlite3_step function call info indicating
// 'prepare statement error' vs 'execute statement error'
// - Android plugin with androidDatabaseImplementation: 2 setting includes the sqlite error code
// - Windows plugin also includes info indicating 'prepare statement error' vs
// 'execute statement error', along with sqlite error code in case of
// 'execute statement error'
// GENERAL NOTE: ERROR MESSAGES are subject to improvements and other possible changes.
it(suiteName + 'syntax error: command with misspelling', function(done) {
var db = openDatabase("Syntax-error-test.db", "1.0", "Demo", DEFAULT_SIZE);
expect(db).toBeDefined();
// VERIFY that an error object was received in the end
var sqlerror = null;
db.transaction(function(tx) {
// syntax error due to misspelling:
tx.executeSql('SLCT 1 ', [], function(tx) {
// NOT EXPECTED:
expect(false).toBe(true);
throw new Error('abort tx');
}, function(tx, error) {
sqlerror = error;
expect(error).toBeDefined();
expect(error.code).toBeDefined();
expect(error.message).toBeDefined();
// error.hasOwnProperty('message') apparently NOT WORKING on
// WebKit Web SQL on Android 5.x/... or iOS 10.x/...:
if (!isWebSql || isWindows || (isAndroid && (/Android 4/.test(navigator.userAgent))))
expect(error.hasOwnProperty('message')).toBe(true);
if (!isWebSql && (isBrowser || isWindows || (isAndroid && isImpl2)))
expect(error.code).toBe(0);
else
expect(error.code).toBe(5);
if (isWebSql && !(/Android 4.[1-3]/.test(navigator.userAgent)))
expect(error.message).toMatch(/could not prepare statement.*1 near \"SLCT\": syntax error/);
else if (isWindows)
expect(error.message).toMatch(/Error preparing an SQLite statement/);
else if (!isWebSql && !isWindows && isAndroid && !isImpl2)
expect(error.message).toMatch(/sqlite3_prepare_v2 failure:.*near \"SLCT\": syntax error/);
else if (!isWebSql && !isWindows && isAndroid && isImpl2)
expect(error.message).toMatch(/near \"SLCT\": syntax error.*code 1.*while compiling: SLCT 1/);
else
expect(error.message).toMatch(/near \"SLCT\": syntax error/);
// FAIL transaction & check reported transaction error:
return true;
});
}, function (error) {
expect(!!sqlerror).toBe(true); // VERIFY the SQL error callback was triggered
expect(error).toBeDefined();
expect(error.code).toBeDefined();
expect(error.message).toBeDefined();
// error.hasOwnProperty('message') apparently NOT WORKING on
// WebKit Web SQL on Android 5.x/... or iOS 10.x/...:
if (!isWebSql || isWindows || (isAndroid && (/Android 4/.test(navigator.userAgent))))
expect(error.hasOwnProperty('message')).toBe(true);
if (isWebSql || isBrowser || isWindows || (isAndroid && isImpl2))
expect(error.code).toBe(0);
else
expect(error.code).toBe(5);
if (isWebSql)
expect(error.message).toMatch(/callback raised an exception.*or.*error callback did not return false/);
else if (isWindows)
expect(error.message).toMatch(/error callback did not return false.*Error preparing an SQLite statement/);
else
expect(error.message).toMatch(/error callback did not return false.*syntax error/);
isWebSql ? done() : db.close(done, done);
}, function() {
// NOT EXPECTED:
expect(false).toBe(true);
isWebSql ? done() : db.close(done, done);
});
}, MYTIMEOUT);
it(suiteName + 'syntax error: comma after a field name', function(done) {
var db = openDatabase('comma-after-field-name-error-test.db');
expect(db).toBeDefined();
// VERIFY that an error object was received in the end
var sqlerror = null;
db.transaction(function(tx) {
// This insertion has a SQL syntax error
tx.executeSql('SELECT name, from Users', [], function(tx) {
// NOT EXPECTED:
expect(false).toBe(true);
throw new Error('abort tx');
}, function(tx, error) {
sqlerror = error;
expect(error).toBeDefined();
expect(error.code).toBeDefined();
expect(error.message).toBeDefined();
// error.hasOwnProperty('message') apparently NOT WORKING on
// WebKit Web SQL on Android 5.x/... or iOS 10.x/...:
if (!isWebSql || isWindows || (isAndroid && (/Android 4/.test(navigator.userAgent))))
expect(error.hasOwnProperty('message')).toBe(true);
if (!isWebSql && (isBrowser || isWindows || (isAndroid && isImpl2)))
expect(error.code).toBe(0);
else
expect(error.code).toBe(5);
if (isWebSql && !(/Android 4.[1-3]/.test(navigator.userAgent)))
expect(error.message).toMatch(/could not prepare statement.*1 near \"from\": syntax error/);
else if (isWindows)
expect(error.message).toMatch(/Error preparing an SQLite statement/);
else
expect(error.message).toMatch(/near \"from\": syntax error/);
// FAIL transaction & check reported transaction error:
return true;
});
}, function (error) {
expect(!!sqlerror).toBe(true); // VERIFY the SQL error callback was triggered
expect(error).toBeDefined();
expect(error.code).toBeDefined();
expect(error.message).toBeDefined();
// error.hasOwnProperty('message') apparently NOT WORKING on
// WebKit Web SQL on Android 5.x/... or iOS 10.x/...:
if (!isWebSql || isWindows || (isAndroid && (/Android 4/.test(navigator.userAgent))))
expect(error.hasOwnProperty('message')).toBe(true);
if (isBrowser || isWindows || isWebSql || (isAndroid && isImpl2))
expect(error.code).toBe(0);
else
expect(error.code).toBe(5);
if (isWebSql)
expect(error.message).toMatch(/callback raised an exception.*or.*error callback did not return false/);
else if (isWindows)
expect(error.message).toMatch(/error callback did not return false.*Error preparing an SQLite statement/);
else
expect(error.message).toMatch(/error callback did not return false.*syntax error/);
isWebSql ? done() : db.close(done, done);
}, function() {
// NOT EXPECTED:
expect(false).toBe(true);
isWebSql ? done() : db.close(done, done);
});
}, MYTIMEOUT);
it(suiteName + 'INSERT with VALUES in the wrong place (and with a trailing space) [TBD "incomplete input" vs "syntax error" message IGNORED on (WebKit) Web SQL on Android 7.0(+) & iOS 12.0(+)]', function(done) {
var db = openDatabase("INSERT-Syntax-error-test.db", "1.0", "Demo", DEFAULT_SIZE);
expect(db).toBeDefined();
// VERIFY that an error object was received in the end
var sqlerror = null;
db.transaction(function(tx) {
tx.executeSql('DROP TABLE IF EXISTS test_table');
tx.executeSql('CREATE TABLE IF NOT EXISTS test_table (data unique)');
// This insertion has a SQL syntax error
tx.executeSql('INSERT INTO test_table (data) VALUES ', [123], function(tx) {
// NOT EXPECTED:
expect(false).toBe(true);
throw new Error('abort tx');
}, function(tx, error) {
sqlerror = error;
expect(error).toBeDefined();
expect(error.code).toBeDefined();
expect(error.message).toBeDefined();
// error.hasOwnProperty('message') apparently NOT WORKING on
// WebKit Web SQL on Android 5.x/... or iOS 10.x/...:
if (!isWebSql || isWindows || (isAndroid && (/Android 4/.test(navigator.userAgent))))
expect(error.hasOwnProperty('message')).toBe(true);
if (!isWebSql && (isBrowser || isWindows || (isAndroid && isImpl2)))
expect(error.code).toBe(0);
else
expect(error.code).toBe(5);
if (isWebSql && (isAppleMobileOS || /Android [7-9]/.test(navigator.userAgent) || /Android 1/.test(navigator.userAgent)))
// TBD incomplete input vs syntax error message IGNORED on Android 7.0(+) & iOS 12.0(+)
expect(error.message).toMatch(/could not prepare statement.*/);
else if (isWebSql && !isBrowser && !(/Android 4.[1-3]/.test(navigator.userAgent)))
expect(error.message).toMatch(/could not prepare statement.*1 near \"VALUES\": syntax error/);
else if (isWebSql && isBrowser)
expect(error.message).toMatch(/could not prepare statement.*1 incomplete input/);
else if (isWebSql)
expect(error.message).toMatch(/near \"VALUES\": syntax error/);
else if (isWindows)
expect(error.message).toMatch(/Error preparing an SQLite statement/);
else if (isAndroid && !isImpl2)
expect(error.message).toMatch(/sqlite3_prepare_v2 failure:.*incomplete input/);
else if (isAndroid && isImpl2)
// TBD more general pattern for Android 9 vs ...
expect(error.message).toMatch(/code 1.*while compiling: INSERT INTO test_table/);
else
expect(error.message).toMatch(/incomplete input/);
// FAIL transaction & check reported transaction error:
return true;
});
}, function (error) {
expect(!!sqlerror).toBe(true); // VERIFY the SQL error callback was triggered
expect(error).toBeDefined();
expect(error.code).toBeDefined();
expect(error.message).toBeDefined();
// error.hasOwnProperty('message') apparently NOT WORKING on
// WebKit Web SQL on Android 5.x/... or iOS 10.x/...:
if (!isWebSql || isWindows || (isAndroid && (/Android 4/.test(navigator.userAgent))))
expect(error.hasOwnProperty('message')).toBe(true);
if (isWebSql || isBrowser || isWindows || (isAndroid && isImpl2))
expect(error.code).toBe(0);
else
expect(error.code).toBe(5);
if (isWebSql)
expect(error.message).toMatch(/callback raised an exception.*or.*error callback did not return false/);
else if (isWindows)
expect(error.message).toMatch(/error callback did not return false.*Error preparing an SQLite statement/);
else if (isAndroid && isImpl2)
// TBD more general pattern for Android 9 vs ...
expect(error.message).toMatch(/error callback did not return false.*code 1/);
else
expect(error.message).toMatch(/error callback did not return false.*incomplete input/);
isWebSql ? done() : db.close(done, done);
}, function() {
// NOT EXPECTED:
expect(false).toBe(true);
isWebSql ? done() : db.close(done, done);
});
}, MYTIMEOUT);
it(suiteName + 'constraint violation', function(done) {
var db = openDatabase("Constraint-violation-test.db", "1.0", "Demo", DEFAULT_SIZE);
expect(db).toBeDefined();
// VERIFY that an error object was received in the end
var sqlerror = null;
db.transaction(function(tx) {
tx.executeSql('DROP TABLE IF EXISTS test_table');
tx.executeSql('CREATE TABLE IF NOT EXISTS test_table (data unique)');
// First INSERT OK:
tx.executeSql("INSERT INTO test_table (data) VALUES (?)", [123], null, function(tx, error) {
// NOT EXPECTED:
expect(false).toBe(true);
expect(error.message).toBe('--');
});
// Second INSERT will violate the unique constraint:
tx.executeSql('INSERT INTO test_table (data) VALUES (?)', [123], function(tx) {
// NOT EXPECTED:
expect(false).toBe(true);
throw new Error('abort tx');
}, function(tx, error) {
sqlerror = error;
expect(error).toBeDefined();
expect(error.code).toBeDefined();
expect(error.message).toBeDefined();
// error.hasOwnProperty('message') apparently NOT WORKING on
// WebKit Web SQL on Android 5.x/... or iOS 10.x/...:
if (!isWebSql || isWindows || (isAndroid && (/Android 4/.test(navigator.userAgent))))
expect(error.hasOwnProperty('message')).toBe(true);
if (isWebSql && (!isAndroid || /Android 4.[1-3]/.test(navigator.userAgent)))
expect(true).toBe(true); // SKIP for iOS (WebKit) & Android 4.1-4.3 (WebKit) Web SQL
else if (isBrowser || isWindows)
expect(error.code).toBe(0);
else
expect(error.code).toBe(6);
// (WebKit) Web SQL (Android/iOS) possibly with a missing 'r'
if (isWebSql && /Android 4.[1-3]/.test(navigator.userAgent))
expect(error.message).toMatch(/column data is not unique/);
else if (isWebSql && isAndroid)
expect(error.message).toMatch(/could not execute statement due to a constr?aint failure.*19.*constraint failed/);
else if (isWebSql)
expect(error.message).toMatch(/constr?aint fail/); // [possibly missing letter on iOS (WebKit) Web SQL]
else if (isWindows)
expect(error.message).toMatch(/SQLite3 step error result code: 1/);
else if (isAndroid && !isImpl2)
expect(error.message).toMatch(/sqlite3_step failure: UNIQUE constraint failed: test_table\.data/);
else if (isAndroid && isImpl2)
expect(error.message).toMatch(/constraint failure/);
else
expect(error.message).toMatch(/UNIQUE constraint failed: test_table\.data/);
// FAIL transaction & check reported transaction error:
return true;
});
}, function (error) {
expect(!!sqlerror).toBe(true); // VERIFY the SQL error callback was triggered
expect(error).toBeDefined();
expect(error.code).toBeDefined();
expect(error.message).toBeDefined();
// error.hasOwnProperty('message') apparently NOT WORKING on
// WebKit Web SQL on Android 5.x/... or iOS 10.x/...:
if (!isWebSql || isWindows || (isAndroid && (/Android 4/.test(navigator.userAgent))))
expect(error.hasOwnProperty('message')).toBe(true);
if (isWebSql || isBrowser || isWindows)
expect(error.code).toBe(0);
else
expect(error.code).toBe(6);
if (isWebSql)
expect(error.message).toMatch(/callback raised an exception.*or.*error callback did not return false/);
else if (isWindows)
expect(error.message).toMatch(/error callback did not return false.*SQLite3 step error result code: 1/);
else
expect(error.message).toMatch(/error callback did not return false.*constraint fail/);
isWebSql ? done() : db.close(done, done);
}, function() {
// NOT EXPECTED:
expect(false).toBe(true);
isWebSql ? done() : db.close(done, done);
});
}, MYTIMEOUT);
it(suiteName + 'SELECT uper("Test") (misspelled function name) [INCORRECT error code WebKit Web SQL & plugin]', function(done) {
var db = openDatabase("Misspelled-function-name-error-test.db", "1.0", "Demo", DEFAULT_SIZE);
expect(db).toBeDefined();
// VERIFY that an error object was received in the end
var sqlerror = null;
db.transaction(function(tx) {
// This insertion has a SQL syntax error
tx.executeSql('SELECT uper("Test")', [], function(tx) {
// NOT EXPECTED:
expect(false).toBe(true);
throw new Error('abort tx');
}, function(tx, error) {
sqlerror = error;
expect(error).toBeDefined();
expect(error.code).toBeDefined();
expect(error.message).toBeDefined();
// error.hasOwnProperty('message') apparently NOT WORKING on
// WebKit Web SQL on Android 5.x/... or iOS 10.x/...:
if (!isWebSql || isWindows || (isAndroid && (/Android 4/.test(navigator.userAgent))))
expect(error.hasOwnProperty('message')).toBe(true);
if (!isWebSql && (isBrowser || isWindows || (isAndroid && isImpl2)))
expect(error.code).toBe(0);
else
expect(error.code).toBe(5);
// ACTUAL WebKit Web SQL vs plugin error.message
if (isWebSql && !(/Android 4.[1-3]/.test(navigator.userAgent)))
expect(error.message).toMatch(/could not prepare statement.*1 no such function: uper/);
else if (isWindows)
expect(error.message).toMatch(/Error preparing an SQLite statement/);
else if (!isWebSql && !isWindows && isAndroid && !isImpl2)
expect(error.message).toMatch(/sqlite3_prepare_v2 failure:.*no such function: uper/);
else if (!isWebSql && !isWindows && isAndroid && isImpl2)
expect(error.message).toMatch(/no such function: uper.*code 1/);
else
expect(error.message).toMatch(/no such function: uper/);
// FAIL transaction & check reported transaction error:
return true;
});
}, function (error) {
expect(!!sqlerror).toBe(true); // VERIFY the SQL error callback was triggered
expect(error).toBeDefined();
expect(error.code).toBeDefined();
expect(error.message).toBeDefined();
// error.hasOwnProperty('message') apparently NOT WORKING on
// WebKit Web SQL on Android 5.x/... or iOS 10.x/...:
if (!isWebSql || isWindows || (isAndroid && (/Android 4/.test(navigator.userAgent))))
expect(error.hasOwnProperty('message')).toBe(true);
if (isWebSql || isBrowser || isWindows || (isAndroid && isImpl2))
expect(error.code).toBe(0);
else
expect(error.code).toBe(5);
if (isWebSql)
expect(error.message).toMatch(/callback raised an exception.*or.*error callback did not return false/);
else if (isWindows)
expect(error.message).toMatch(/error callback did not return false.*Error preparing an SQLite statement/);
else
expect(error.message).toMatch(/error callback did not return false.*no such function: uper/);
isWebSql ? done() : db.close(done, done);
}, function() {
// NOT EXPECTED:
expect(false).toBe(true);
isWebSql ? done() : db.close(done, done);
});
}, MYTIMEOUT);
it(suiteName + 'SELECT FROM bogus table (other database error) [INCORRECT error code WebKit Web SQL & plugin]', function(done) {
var db = openDatabase("SELECT-FROM-bogus-table-error-test.db", "1.0", "Demo", DEFAULT_SIZE);
expect(db).toBeDefined();
// VERIFY that an error object was received in the end
var sqlerror = null;
db.transaction(function(tx) {
tx.executeSql('DROP TABLE IF EXISTS BogusTable');
// Attempt to SELECT FROM a bogus table:
tx.executeSql('SELECT * FROM BogusTable', [], function(ignored1, ignored2) {
// NOT EXPECTED:
expect(false).toBe(true);
throw new Error('abort tx');
}, function(tx, error) {
sqlerror = error;
expect(error).toBeDefined();
expect(error.code).toBeDefined();
expect(error.message).toBeDefined();
// error.hasOwnProperty('message') apparently NOT WORKING on
// WebKit Web SQL on Android 5.x/... or iOS 10.x/...:
if (!isWebSql || isWindows || (isAndroid && (/Android 4/.test(navigator.userAgent))))
expect(error.hasOwnProperty('message')).toBe(true);
if (!isWebSql && (isBrowser || isWindows || (isAndroid && isImpl2)))
expect(error.code).toBe(0);
else
expect(error.code).toBe(5);
if (isWebSql && !(/Android 4.[1-3]/.test(navigator.userAgent)))
expect(error.message).toMatch(/could not prepare statement.*1 no such table: BogusTable/);
else if (isWindows)
expect(error.message).toMatch(/Error preparing an SQLite statement/);
else if (!isWebSql && !isWindows && isAndroid && !isImpl2)
expect(error.message).toMatch(/sqlite3_prepare_v2 failure:.*no such table: BogusTable/);
else if (!isWebSql && !isWindows && isAndroid && isImpl2)
expect(error.message).toMatch(/no such table: BogusTable.*code 1/);
else
expect(error.message).toMatch(/no such table: BogusTable/);
// FAIL transaction & check reported transaction error:
return true;
});
}, function (error) {
expect(!!sqlerror).toBe(true); // VERIFY the SQL error callback was triggered
expect(error).toBeDefined();
expect(error.code).toBeDefined();
expect(error.message).toBeDefined();
// error.hasOwnProperty('message') apparently NOT WORKING on
// WebKit Web SQL on Android 5.x/... or iOS 10.x/...:
if (!isWebSql || isWindows || (isAndroid && (/Android 4/.test(navigator.userAgent))))
expect(error.hasOwnProperty('message')).toBe(true);
if (isWebSql || isBrowser || isWindows || (isAndroid && isImpl2))
expect(error.code).toBe(0);
else
expect(error.code).toBe(5);
if (isWebSql)
expect(error.message).toMatch(/callback raised an exception.*or.*error callback did not return false/);
else if (isWindows)
expect(error.message).toMatch(/error callback did not return false.*Error preparing an SQLite statement/);
else
expect(error.message).toMatch(/error callback did not return false.*no such table: BogusTable/);
isWebSql ? done() : db.close(done, done);
}, function() {
// NOT EXPECTED:
expect(false).toBe(true);
isWebSql ? done() : db.close(done, done);
});
}, MYTIMEOUT);
it(suiteName + 'INSERT missing column [INCORRECT error code WebKit Web SQL & plugin]', function(done) {
var db = openDatabase("INSERT-missing-column-test.db", "1.0", "Demo", DEFAULT_SIZE);
expect(db).toBeDefined();
// VERIFY that an error object was received in the end
var sqlerror = null;
db.transaction(function(tx) {
tx.executeSql('DROP TABLE IF EXISTS test_table');
tx.executeSql('CREATE TABLE IF NOT EXISTS test_table (data1, data2)');
tx.executeSql('INSERT INTO test_table VALUES (?)', ['abcdef'], function(tx) {
// NOT EXPECTED:
expect(false).toBe(true);
throw new Error('abort tx');
}, function(tx, error) {
sqlerror = error;
expect(error).toBeDefined();
expect(error.code).toBeDefined();
expect(error.message).toBeDefined();
// error.hasOwnProperty('message') apparently NOT WORKING on
// WebKit Web SQL on Android 5.x/... or iOS 10.x/...:
if (!isWebSql || isWindows || (isAndroid && (/Android 4/.test(navigator.userAgent))))
expect(error.hasOwnProperty('message')).toBe(true);
if (!isWebSql && (isBrowser || isWindows || (isAndroid && isImpl2)))
expect(error.code).toBe(0);
else
expect(error.code).toBe(5);
if (isWebSql && !(/Android 4.[1-3]/.test(navigator.userAgent)))
expect(error.message).toMatch(/could not prepare statement.*1 table test_table has 2 columns but 1 values were supplied/);
else if (isWindows)
expect(error.message).toMatch(/Error preparing an SQLite statement/);
else if (!isWebSql && !isWindows && isAndroid && !isImpl2)
expect(error.message).toMatch(/sqlite3_prepare_v2 failure:.*table test_table has 2 columns but 1 values were supplied/);
else if (!isWebSql && !isWindows && isAndroid && isImpl2)
expect(error.message).toMatch(/table test_table has 2 columns but 1 values were supplied.*code 1.*while compiling: INSERT INTO test_table/);
else
expect(error.message).toMatch(/table test_table has 2 columns but 1 values were supplied/);
// FAIL transaction & check reported transaction error:
return true;
});
}, function (error) {
expect(!!sqlerror).toBe(true); // VERIFY the SQL error callback was triggered
expect(error).toBeDefined();
expect(error.code).toBeDefined();
expect(error.message).toBeDefined();
// error.hasOwnProperty('message') apparently NOT WORKING on
// WebKit Web SQL on Android 5.x/... or iOS 10.x/...:
if (!isWebSql || isWindows || (isAndroid && (/Android 4/.test(navigator.userAgent))))
expect(error.hasOwnProperty('message')).toBe(true);
if (isWebSql || isBrowser || isWindows || (isAndroid && isImpl2))
expect(error.code).toBe(0);
else
expect(error.code).toBe(5);
if (isWebSql)
expect(error.message).toMatch(/callback raised an exception.*or.*error callback did not return false/);
else if (isWindows)
expect(error.message).toMatch(/error callback did not return false.*Error preparing an SQLite statement/);
else
expect(error.message).toMatch(/error callback did not return false.*table test_table has 2 columns but 1 values were supplied/);
isWebSql ? done() : db.close(done, done);
}, function() {
// NOT EXPECTED:
expect(false).toBe(true);
isWebSql ? done() : db.close(done, done);
});
}, MYTIMEOUT);
it(suiteName + 'INSERT wrong column name [INCORRECT error code WebKit Web SQL & plugin]', function(done) {
var db = openDatabase("INSERT-wrong-column-name-test.db", "1.0", "Demo", DEFAULT_SIZE);
expect(db).toBeDefined();
// VERIFY that an error object was received in the end
var sqlerror = null;
db.transaction(function(tx) {
tx.executeSql('DROP TABLE IF EXISTS test_table');
tx.executeSql('CREATE TABLE IF NOT EXISTS test_table (data1)');
tx.executeSql('INSERT INTO test_table (wrong_column) VALUES (?)', ['abcdef'], function(tx) {
// NOT EXPECTED:
expect(false).toBe(true);
throw new Error('abort tx');
}, function(tx, error) {
sqlerror = error;
expect(error).toBeDefined();
expect(error.code).toBeDefined();
expect(error.message).toBeDefined();
// error.hasOwnProperty('message') apparently NOT WORKING on
// WebKit Web SQL on Android 5.x/... or iOS 10.x/...:
if (!isWebSql || isWindows || (isAndroid && (/Android 4/.test(navigator.userAgent))))
expect(error.hasOwnProperty('message')).toBe(true);
if (!isWebSql && (isBrowser || isWindows || (isAndroid && isImpl2)))
expect(error.code).toBe(0);
else
expect(error.code).toBe(5);
if (isWebSql && !(/Android 4.[1-3]/.test(navigator.userAgent)))
expect(error.message).toMatch(/could not prepare statement.*1 table test_table has no column named wrong_column/);
else if (isWindows)
expect(error.message).toMatch(/Error preparing an SQLite statement/);
else if (!isWebSql && !isWindows && isAndroid && !isImpl2)
expect(error.message).toMatch(/sqlite3_prepare_v2 failure:.*table test_table has no column named wrong_column/);
else if (!isWebSql && !isWindows && isAndroid && isImpl2)
expect(error.message).toMatch(/table test_table has no column named wrong_column.*code 1.*while compiling: INSERT INTO test_table/);
else
expect(error.message).toMatch(/table test_table has no column named wrong_column/);
// FAIL transaction & check reported transaction error:
return true;
});
}, function (error) {
expect(!!sqlerror).toBe(true); // VERIFY the SQL error callback was triggered
expect(error).toBeDefined();
expect(error.code).toBeDefined();
expect(error.message).toBeDefined();
// error.hasOwnProperty('message') apparently NOT WORKING on
// WebKit Web SQL on Android 5.x/... or iOS 10.x/...:
if (!isWebSql || isWindows || (isAndroid && (/Android 4/.test(navigator.userAgent))))
expect(error.hasOwnProperty('message')).toBe(true);
if (isWebSql || isBrowser || isWindows || (isAndroid && isImpl2))
expect(error.code).toBe(0);
else
expect(error.code).toBe(5);
if (isWebSql)
expect(error.message).toMatch(/callback raised an exception.*or.*error callback did not return false/);
else if (isWindows)
expect(error.message).toMatch(/error callback did not return false.*Error preparing an SQLite statement/);
else
expect(error.message).toMatch(/error callback did not return false.*table test_table has no column named wrong_column/);
isWebSql ? done() : db.close(done, done);
}, function() {
// NOT EXPECTED:
expect(false).toBe(true);
isWebSql ? done() : db.close(done, done);
});
}, MYTIMEOUT);
// NOTE: For some reason the Android/iOS (WebKit) Web SQL implementation
// claims to detect the error at the "prepare statement" stage while the
// plugin detects the error at the "execute statement" stage.
it(suiteName + 'CREATE VIRTUAL TABLE USING bogus module (other database error) [INCORRECT error code WebKit Web SQL & plugin]', function(done) {
var db = openDatabase("create-virtual-table-using-bogus-module-error-test.db", "1.0", "Demo", DEFAULT_SIZE);
expect(db).toBeDefined();
// VERIFY that an error object was received in the end
var sqlerror = null;
db.transaction(function(tx) {
tx.executeSql('DROP TABLE IF EXISTS test_table');
// Attempt to use a bogus module:
tx.executeSql('CREATE VIRTUAL TABLE test_table USING bogus_module (data)', [], function(ignored1, ignored2) {
// NOT EXPECTED:
expect(false).toBe(true);
throw new Error('abort tx');
}, function(tx, error) {
sqlerror = error;
expect(error).toBeDefined();
expect(error.code).toBeDefined();
expect(error.message).toBeDefined();
// error.hasOwnProperty('message') apparently NOT WORKING on
// WebKit Web SQL on Android 5.x/... or iOS 10.x/...:
if (!isWebSql || isWindows || (isAndroid && (/Android 4/.test(navigator.userAgent))))
expect(error.hasOwnProperty('message')).toBe(true);
if (!isWebSql && (isBrowser || isWindows || (isAndroid && isImpl2)))
expect(error.code).toBe(0);
else
expect(error.code).toBe(5);
if (isWebSql && isAndroid && !(/Android 4.[1-3]/.test(navigator.userAgent)))
expect(error.message).toMatch(/could not prepare statement.*not authorized/);
else if (isWebSql && isAndroid)
expect(error.message).toMatch(/not authorized/);
else if (isWebSql && (isBrowser && (/Chrome/.test(navigator.userAgent))))
expect(error.message).toMatch(/could not prepare statement.*23 not authorized/);
else if (isWebSql) // [iOS (WebKit) Web SQL]
expect(error.message).toMatch(/could not prepare statement.*1 not authorized/);
else if (isWindows)
expect(error.message).toMatch(/SQLite3 step error result code: 1/);
else if (isAndroid && !isImpl2)
expect(error.message).toMatch(/sqlite3_step failure: no such module: bogus/);
else if (isAndroid && isImpl2)
expect(error.message).toMatch(/no such module: bogus.*code 1/);
else
expect(error.message).toMatch(/no such module: bogus/);
// FAIL transaction & check reported transaction error:
return true;
});
}, function (error) {
expect(!!sqlerror).toBe(true); // VERIFY the SQL error callback was triggered
expect(error).toBeDefined();
expect(error.code).toBeDefined();
expect(error.message).toBeDefined();
// error.hasOwnProperty('message') apparently NOT WORKING on
// WebKit Web SQL on Android 5.x/... or iOS 10.x/...:
if (!isWebSql || isWindows || (isAndroid && (/Android 4/.test(navigator.userAgent))))
expect(error.hasOwnProperty('message')).toBe(true);
if (isWebSql || isBrowser || isWindows || (isAndroid && isImpl2))
expect(error.code).toBe(0);
else
expect(error.code).toBe(5);
if (isWebSql)
expect(error.message).toMatch(/callback raised an exception.*or.*error callback did not return false/);
else if (isWindows)
expect(error.message).toMatch(/error callback did not return false.*SQLite3 step error result code: 1/);
else
expect(error.message).toMatch(/error callback did not return false.*no such module: bogus/);
isWebSql ? done() : db.close(done, done);
}, function() {
// NOT EXPECTED:
expect(false).toBe(true);
isWebSql ? done() : db.close(done, done);
});
}, MYTIMEOUT);
// TESTS with no SQL error handler:
it(suiteName + 'transaction.executeSql syntax error (command with misspelling) with no SQL error handler', function(done) {
db = openDatabase('tx-sql-syntax-error-with-no-sql-error-handler-test.db');
db.transaction(function(transaction) {
transaction.executeSql('SLCT 1');
}, function(error) {
// EXPECTED RESULT:
expect(error).toBeDefined();
expect(error.code).toBeDefined();
expect(error.message).toBeDefined();
// error.hasOwnProperty('message') apparently NOT WORKING on
// WebKit Web SQL on Android 5.x/... or iOS 10.x/...:
if (!isWebSql || isWindows || (isAndroid && (/Android 4/.test(navigator.userAgent))))
expect(error.hasOwnProperty('message')).toBe(true);
if (!isWebSql && (isBrowser || isWindows || (isAndroid && isImpl2)))
expect(error.code).toBe(0);
else
expect(error.code).toBe(5);
if (isWebSql && !(/Android 4.[1-3]/.test(navigator.userAgent)))
expect(error.message).toMatch(/could not prepare statement.*1 near \"SLCT\": syntax error/);
else if (isWindows)
expect(error.message).toMatch(/a statement with no error handler failed: Error preparing an SQLite statement/);
else if (!isWebSql && !isWindows && isAndroid && !isImpl2)
expect(error.message).toMatch(/sqlite3_prepare_v2 failure:.*near \"SLCT\": syntax error/);
else if (!isWebSql && !isWindows && isAndroid && isImpl2)
expect(error.message).toMatch(/a statement with no error handler failed: near \"SLCT\": syntax error.*code 1.*while compiling: SLCT 1/);
else if (!isWebSql) // [iOS/macOS plugin]
expect(error.message).toMatch(/a statement with no error handler failed.*near \"SLCT\": syntax error/);
else
expect(error.message).toMatch(/near \"SLCT\": syntax error/);
isWebSql ? done() : db.close(done, done);
}, function() {
// NOT EXPECTED:
expect(false).toBe(true);
isWebSql ? done() : db.close(done, done);
})
}, MYTIMEOUT);
it(suiteName + 'transaction.executeSql constraint violation with no SQL error handler', function(done) {
var db = openDatabase("Constraint-violation-with-no-sql-error-handler.db", "1.0", "Demo", DEFAULT_SIZE);
db.transaction(function(tx) {
tx.executeSql('DROP TABLE IF EXISTS test_table');
tx.executeSql('CREATE TABLE IF NOT EXISTS test_table (data unique)');
// First INSERT OK:
tx.executeSql('INSERT INTO test_table (data) VALUES (?)', [123]);
// Second INSERT will violate the unique constraint:
tx.executeSql('INSERT INTO test_table (data) VALUES (?)', [123]);
}, function (error) {
expect(error).toBeDefined();
expect(error.code).toBeDefined();
expect(error.message).toBeDefined();
// error.hasOwnProperty('message') apparently NOT WORKING on
// WebKit Web SQL on Android 5.x/... or iOS 10.x/...:
if (!isWebSql || isWindows || (isAndroid && (/Android 4/.test(navigator.userAgent))))
expect(error.hasOwnProperty('message')).toBe(true);
if (isWebSql && (!isAndroid || /Android 4.[1-3]/.test(navigator.userAgent)))
expect(true).toBe(true); // SKIP for iOS (WebKit) & Android 4.1-4.3 (WebKit) Web SQL
else if (isBrowser || isWindows)
expect(error.code).toBe(0);
else
expect(error.code).toBe(6);
// (WebKit) Web SQL (Android/iOS) possibly with a missing 'r'
if (isWebSql && /Android 4.[1-3]/.test(navigator.userAgent))
expect(error.message).toMatch(/column data is not unique/);
else if (isWebSql && isAndroid)
expect(error.message).toMatch(/could not execute statement due to a constr?aint failure.*19.*constraint failed/);
else if (isWebSql)
expect(error.message).toMatch(/constr?aint fail/);
else if (isWindows)
expect(error.message).toMatch(/a statement with no error handler failed: SQLite3 step error result code: 1/);
else if (isAndroid && !isImpl2)
expect(error.message).toMatch(/a statement with no error handler failed: sqlite3_step failure: UNIQUE constraint failed: test_table\.data/);
else if (isAndroid && isImpl2)
expect(error.message).toMatch(/a statement with no error handler failed:.*constraint failure/);
else if (isBrowser)
expect(error.message).toMatch(/a statement with no error handler failed: Error: UNIQUE constraint failed: test_table\.data/);
else
expect(error.message).toMatch(/a statement with no error handler failed: UNIQUE constraint failed: test_table\.data/);
isWebSql ? done() : db.close(done, done);
}, function() {
// NOT EXPECTED:
expect(false).toBe(true);
isWebSql ? done() : db.close(done, done);
});
}, MYTIMEOUT);
});
}
}
if (window.hasBrowser) mytests();
else exports.defineAutoTests = mytests;
/* vim: set expandtab : */

View File

@ -0,0 +1,116 @@
/* 'use strict'; */
var MYTIMEOUT = 12000;
// NOTE: DEFAULT_SIZE wanted depends on type of browser
var isWindows = /MSAppHost/.test(navigator.userAgent);
var isAndroid = !isWindows && /Android/.test(navigator.userAgent);
var isFirefox = /Firefox/.test(navigator.userAgent);
var isWebKitBrowser = !isWindows && !isAndroid && /Safari/.test(navigator.userAgent);
var isBrowser = isWebKitBrowser || isFirefox;
var isEdgeBrowser = isBrowser && (/Edge/.test(navigator.userAgent));
var isChromeBrowser = isBrowser && !isEdgeBrowser && (/Chrome/.test(navigator.userAgent));
var isSafariBrowser = isWebKitBrowser && !isEdgeBrowser && !isChromeBrowser;
// should avoid popups (Safari seems to count 2x)
var DEFAULT_SIZE = isSafariBrowser ? 2000000 : 5000000;
// FUTURE TBD: 50MB should be OK on Chrome and some other test browsers.
// NOTE: While in certain version branches there is no difference between
// the default Android implementation and implementation #2,
// this test script will also apply the androidLockWorkaround: 1 option
// in case of implementation #2.
var scenarioList = [
isAndroid ? 'Plugin-implementation-default' : 'Plugin',
'HTML5',
'Plugin-implementation-2'
];
var scenarioCount = (!!window.hasWebKitWebSQL) ? (isAndroid ? 3 : 2) : 1;
var mytests = function() {
for (var i=0; i<scenarioCount; ++i) {
describe(scenarioList[i] + ': db tx multiple update test(s)', function() {
var scenarioName = scenarioList[i];
var suiteName = scenarioName + ': ';
var isWebSql = (i === 1);
var isImpl2 = (i === 2);
// NOTE: MUST be defined in function scope, NOT outer scope:
var openDatabase = function(name, ignored1, ignored2, ignored3) {
if (isImpl2) {
return window.sqlitePlugin.openDatabase({
// prevent reuse of database from default db implementation:
name: 'i2-'+name,
androidDatabaseImplementation: 2,
androidLockWorkaround: 1,
location: 1
});
}
if (isWebSql) {
return window.openDatabase(name, "1.0", "Demo", DEFAULT_SIZE);
} else {
return window.sqlitePlugin.openDatabase({name: name, location: 0});
}
}
// ref: litehelpers/Cordova-sqlite-storage#128
// Was caused by a failure to create temporary transaction files on WP8.
// Workaround by Mark Oppenheim mailto:mark.oppenheim@mnetics.co.uk
// solved the issue for WP8.
// @brodybits noticed similar issue possible with Android-sqlite-connector
// if the Android-sqlite-native-driver part is not built correctly.
it(suiteName + 'Multiple updates with key (evidently needs temporary transaction files to work)', function (done) {
// if (!isWebSql && isBrowser) pending('NOT WORKING on browser plugin'); // XXX TBD
var db = openDatabase("MultipleUpdatesWithKey", "1.0",
"Demo", DEFAULT_SIZE);
var updateSuccessCount = 0;
db.transaction(function (tx) {
tx.executeSql('DROP TABLE IF EXISTS Task');
tx.executeSql('CREATE TABLE IF NOT EXISTS Task (id primary key, subject)');
tx.executeSql('INSERT INTO Task VALUES (?,?)', ['928238b3-a227-418f-aa15-12bb1943c1f2', 'test1']);
tx.executeSql('INSERT INTO Task VALUES (?,?)', ['511e3fb7-5aed-4c1a-b1b7-96bf9c5012e2', 'test2']);
tx.executeSql('UPDATE Task SET subject="Send reminder", id="928238b3-a227-418f-aa15-12bb1943c1f2" WHERE id = "928238b3-a227-418f-aa15-12bb1943c1f2"', [], function(tx, res) {
expect(res).toBeDefined();
expect(res.rowsAffected).toEqual(1);
check1 = true;
++updateSuccessCount;
}, function (error) {
// NOT EXPECTED:
expect('1st update failed ' + error.message).toBe(true);
});
tx.executeSql('UPDATE Task SET subject="Task", id="511e3fb7-5aed-4c1a-b1b7-96bf9c5012e2" WHERE id = "511e3fb7-5aed-4c1a-b1b7-96bf9c5012e2"', [], function(tx, res) {
expect(res.rowsAffected).toEqual(1);
++updateSuccessCount;
}, function (error) {
// NOT EXPECTED:
expect('2nd update failed ' + error.message).toBe('--');
});
}, function (error) {
// NOT EXPECTED:
expect('transaction failed: ' + error.message).toBe('--');
done.fail();
}, function () {
// transaction committed ok:
expect(updateSuccessCount).toBe(2);
done();
});
});
});
}
}
if (window.hasBrowser) mytests();
else exports.defineAutoTests = mytests;
/* vim: set expandtab : */

View File

@ -0,0 +1,376 @@
/* 'use strict'; */
var MYTIMEOUT = 20000;
// NOTE: DEFAULT_SIZE wanted depends on type of browser
var isWindows = /MSAppHost/.test(navigator.userAgent);
var isAndroid = !isWindows && /Android/.test(navigator.userAgent);
var isFirefox = /Firefox/.test(navigator.userAgent);
var isWebKitBrowser = !isWindows && !isAndroid && /Safari/.test(navigator.userAgent);
var isBrowser = isWebKitBrowser || isFirefox;
var isEdgeBrowser = isBrowser && (/Edge/.test(navigator.userAgent));
var isChromeBrowser = isBrowser && !isEdgeBrowser && (/Chrome/.test(navigator.userAgent));
var isSafariBrowser = isWebKitBrowser && !isEdgeBrowser && !isChromeBrowser;
var isMac = !isBrowser && /Macintosh/.test(navigator.userAgent);
var isAppleMobileOS = /iPhone/.test(navigator.userAgent) ||
/iPad/.test(navigator.userAgent) || /iPod/.test(navigator.userAgent);
// should avoid popups (Safari seems to count 2x)
var DEFAULT_SIZE = isSafariBrowser ? 2000000 : 5000000;
// FUTURE TBD: 50MB should be OK on Chrome and some other test browsers.
// NOTE: While in certain version branches there is no difference between
// the default Android implementation and implementation #2,
// this test script will also apply the androidLockWorkaround: 1 option
// in case of implementation #2.
var scenarioList = [
isAndroid ? 'Plugin-implementation-default' : 'Plugin',
'HTML5',
'Plugin-implementation-2'
];
var scenarioCount = (!!window.hasWebKitWebSQL) ? (isAndroid ? 3 : 2) : 1;
var mytests = function() {
for (var i=0; i<scenarioCount; ++i) {
describe(scenarioList[i] + ': db tx sql features test(s)', function() {
var scenarioName = scenarioList[i];
var suiteName = scenarioName + ': ';
var isWebSql = (i === 1);
var isImpl2 = (i === 2);
// NOTE: MUST be defined in proper describe function scope, NOT outer scope:
var openDatabase = function(name, ignored1, ignored2, ignored3) {
if (isImpl2) {
return window.sqlitePlugin.openDatabase({
// prevent reuse of database from default db implementation:
name: 'i2-'+name,
androidDatabaseImplementation: 2,
androidLockWorkaround: 1,
location: 1
});
}
if (isWebSql) {
return window.openDatabase(name, '1.0', 'Test', DEFAULT_SIZE);
} else {
return window.sqlitePlugin.openDatabase({name: name, location: 0});
}
}
// Known to work with:
// - iOS 9 Web SQL
// - Android (default Android-sqlite-connector implementation)
// - iOS & Windows (with newer sqlite3 build)
it(suiteName + 'db readTransaction with a WITH clause', function(done) {
if (isWebSql && !isBrowser) pending('SKIP for Android/iOS (WebKit) Web SQL'); // XXX TBD NOT WORKING on all Android/iOS versions
if (isAndroid && isImpl2) pending('SKIP for android.database implementation'); // NOT WORKING on all versions
var db = openDatabase('tx-with-a-with-clause-test.db', '1.0', 'Test', DEFAULT_SIZE);
db.readTransaction(function(tx) {
tx.executeSql('WITH one(x) AS (SELECT 1) SELECT x FROM one;');
}, function(error) {
// NOT EXPECTED:
expect(false).toBe(true);
expect(JSON.stringify(error)).toBe('---');
// Close (plugin only) & finish:
(isWebSql) ? done() : db.close(done, done);
}, function() {
// EXPECTED RESULT:
expect(true).toBe(true);
// Close (plugin only) & finish:
(isWebSql) ? done() : db.close(done, done);
});
}, MYTIMEOUT);
/* THANKS to @calebeaires: */
it(suiteName + 'create virtual table using FTS3', function(done) {
if (isWebSql && isSafariBrowser) pending('SKIP for (WebKit) Web SQL on Safari browser');
if (isWebSql && isAndroid) pending('SKIP for Android Web SQL');
if (isWebSql && isAppleMobileOS && (/OS 1[1-9]/.test(navigator.userAgent))) pending('SKIP (WebKit) Web SQL on iOS 11(+)');
var db = openDatabase('virtual-table-using-fts3.db', '1.0', 'Test', DEFAULT_SIZE);
expect(db).toBeDefined();
var isCreateOK = false;
var createError = null;
db.transaction(function(tx) {
expect(tx).toBeDefined();
//tx.executeSql('CREATE INDEX liv_index ON book (liv, cap);');
tx.executeSql('DROP TABLE IF EXISTS virtual_book');
tx.executeSql('CREATE VIRTUAL TABLE IF NOT EXISTS virtual_book USING FTS3 (liv, cap, ver, tex, tes);', [], function(tx_ignored, rs_ignored) {
// CREATE OK:
expect(true).toBe(true);
isCreateOK = true;
}, function(tx_ignored, error) {
// NOT EXPECTED:
expect(false).toBe(true);
expect(JSON.stringify(error)).toBe('---');
createError = error;
return true; // report error for both Web SQL & plugin
});
}, function(err) {
// NOTE: CREATE ERROR should have already been reported above.
expect(createError).toBeDefined();
expect(createError).not.toBeNull();
// Close (plugin only) & finish:
(isWebSql) ? done() : db.close(done, done);
}, function() {
// EXPECTED RESULT (verify CREATE was ok):
expect(isCreateOK).toBe(true);
// Close (plugin only) & finish:
(isWebSql) ? done() : db.close(done, done);
});
}, MYTIMEOUT);
// NOTE: looking at sqlite3.c (newer versions), if FTS3 is enabled,
// FTS4 seems to be working as well!
// (thanks again to @calebeaires for this scenario)
it(suiteName + 'create virtual table using FTS4', function(done) {
if (isWebSql) pending('SKIP for Web SQL');
var db = openDatabase('virtual-table-using-fts4.db', '1.0', 'Test', DEFAULT_SIZE);
expect(db).toBeDefined();
var isCreateOK = false;
var createError = null;
db.transaction(function(tx) {
expect(tx).toBeDefined();
//tx.executeSql('CREATE INDEX liv_index ON book (liv, cap);');
tx.executeSql('DROP TABLE IF EXISTS virtual_book');
tx.executeSql('CREATE VIRTUAL TABLE IF NOT EXISTS virtual_book USING FTS4 (liv, cap, ver, tex, tes);', [], function(tx_ignored, rs_ignored) {
// CREATE OK:
expect(true).toBe(true);
isCreateOK = true;
}, function(tx_ignored, error) {
// NOT EXPECTED:
expect(false).toBe(true);
expect(JSON.stringify(error)).toBe('---');
createError = error;
return true; // report error for both Web SQL & plugin
});
}, function(err) {
// NOTE: CREATE ERROR should have already been reported above.
expect(createError).toBeDefined();
expect(createError).not.toBeNull();
// Close (plugin only) & finish:
(isWebSql) ? done() : db.close(done, done);
}, function() {
// EXPECTED RESULT (verify CREATE was ok):
expect(isCreateOK).toBe(true);
// Close (plugin only) & finish:
(isWebSql) ? done() : db.close(done, done);
});
}, MYTIMEOUT);
// now enabled by default in recent SQLite versions
// as spotted in sql.js update history
it(suiteName + 'Basic JSON1 json test', function(done) {
if (isWebSql || isImpl2) pending('SKIP ...'); // XXX TBD ...
var db = openDatabase('basic-json1-json-test.db', '1.0', 'Test', DEFAULT_SIZE);
expect(db).toBeDefined();
db.transaction(function(tx) {
expect(tx).toBeDefined();
// Derived from sample in: https://www.sqlite.org/json1.html
tx.executeSql("SELECT json(?) AS my_json;", [' { "this" : "is", "a": [ "test" ] } '], function(tx, res) {
expect(res.rows.item(0).my_json).toEqual('{"this":"is","a":["test"]}');
//done();
// Close (plugin only) & finish:
(isWebSql) ? done() : db.close(done, done);
}, function(tx, e) {
// NOT EXPECTED (went wrong):
expect(false).toBe(true);
expect(JSON.stringify(e)).toBe('--');
//done();
// Close (plugin only) & finish:
(isWebSql) ? done() : db.close(done, done);
});
});
}, MYTIMEOUT);
// now enabled by default in recent SQLite versions
// as spotted in sql.js update history
it(suiteName + 'JSON1 json_object test', function(done) {
if (isWebSql || isImpl2) pending('SKIP ...'); // XXX TBD ...
var db = openDatabase('json1-json-object-test.db', '1.0', 'Test', DEFAULT_SIZE);
expect(db).toBeDefined();
db.transaction(function(tx) {
expect(tx).toBeDefined();
// Derived from sample in: https://www.sqlite.org/json1.html
tx.executeSql("SELECT json_object(?,?) AS my_object;", ['ex','[52,3.14159]'], function(tx, res) {
// EXPECTED RESULT:
expect(res.rows.item(0).my_object).toEqual('{"ex":"[52,3.14159]"}');
//done();
// Close (plugin only) & finish:
(isWebSql) ? done() : db.close(done, done);
}, function(tx, e) {
// NOT EXPECTED (went wrong):
expect(false).toBe(true);
expect(JSON.stringify(e)).toBe('--');
//done();
// Close (plugin only) & finish:
(isWebSql) ? done() : db.close(done, done);
});
});
}, MYTIMEOUT);
// Test for Cordova-sqlcipher-adapter version (SQLCipher 3.4.0 based on SQLite 3.11.0)
it(suiteName + 'create virtual table using FTS5', function(done) {
//if (isWebSql) pending('SKIP for Web SQL (not implemented)');
pending('SKIP: NOT IMPLEMENTED for this version');
var db = openDatabase('virtual-table-using-fts5.db', '1.0', 'Test', DEFAULT_SIZE);
expect(db).toBeDefined();
var isCreateOK = false;
var createError = null;
db.transaction(function(tx) {
expect(tx).toBeDefined();
//tx.executeSql('CREATE INDEX liv_index ON book (liv, cap);');
tx.executeSql('DROP TABLE IF EXISTS virtual_book');
tx.executeSql('CREATE VIRTUAL TABLE IF NOT EXISTS virtual_book USING FTS5 (liv, cap, ver, tex, tes);', [], function(tx, res) {
// EXPECTED RESULT:
expect(true).toBe(true);
isCreateOK = true;
}, function(tx, error) {
// NOT EXPECTED (went wrong):
expect(false).toBe(true);
expect(JSON.stringify(error)).toBe('---');
createError = error;
return true; // report error for both Web SQL & plugin
});
}, function(err) {
// NOTE: CREATE ERROR should have already been reported above.
expect(createError).toBeDefined();
expect(createError).not.toBeNull();
// Close (plugin only) & finish:
(isWebSql) ? done() : db.close(done, done);
}, function() {
// EXPECTED RESULT (verify CREATE was ok):
expect(isCreateOK).toBe(true);
// Close (plugin only) & finish:
(isWebSql) ? done() : db.close(done, done);
});
}, MYTIMEOUT);
it(suiteName + 'create virtual table using R-Tree', function(done) {
if (isWebSql) pending('SKIP for Web SQL');
if (isAndroid && isImpl2) pending('NOT IMPLEMENTED for all versions of android.database'); // NOT IMPLEMENTED for all versions of Android database (failed in Circle CI)
if (!isWebSql && isBrowser) pending('NOT IMPLEMENTED on plugin for browser platform'); // FUTURE TODO
var db = openDatabase('virtual-table-using-r-tree.db', '1.0', 'Test', DEFAULT_SIZE);
expect(db).toBeDefined();
var isCreateOK = false;
var createError = null;
db.transaction(function(tx) {
expect(tx).toBeDefined();
tx.executeSql('DROP TABLE IF EXISTS demo_index');
// from https://www.sqlite.org/rtree.html
tx.executeSql('CREATE VIRTUAL TABLE IF NOT EXISTS demo_index USING rtree (id, minX, maxX, minY, maxY);', [], function(tx, res) {
// EXPECTED RESULT:
expect(true).toBe(true);
isCreateOK = true;
}, function(err) {
// NOT EXPECTED (went wrong):
expect(false).toBe(true);
expect(JSON.stringify(e)).toBe('--');
createError = error;
});
}, function(err) {
// NOTE: CREATE ERROR should have already been reported above.
expect(createError).toBeDefined();
expect(createError).not.toBeNull();
// Close (plugin only) & finish:
(isWebSql) ? done() : db.close(done, done);
}, function() {
// EXPECTED RESULT (verify CREATE was ok):
expect(isCreateOK).toBe(true);
// Close (plugin only) & finish:
(isWebSql) ? done() : db.close(done, done);
});
}, MYTIMEOUT);
// NOTE: NOT supported by SQLite amalgamation.
// SQLite amalgamation must be rebuilt with
// SQLITE_ENABLE_UPDATE_DELETE_LIMIT defined
// for this feature to work.
xit(suiteName + 'DELETE LIMIT', function(done) {
if (isWebSql) pending('SKIP for Web SQL (NOT IMPLEMENTED)');
if (isWindows) pending('NOT IMPLEMENTED for Windows');
if (isAndroid && !isWebSql) pending('SKIP for Android plugin'); // FUTURE TBD test with newer versions (android.database)
if (isAppleMobileOS || isMac) pending('SKIP for iOS/macOS'); // NOT WORKING on any versions of iOS/macOS (plugin or Web SQL)
var db = openDatabase('delete-limit-test.db', '1.0', 'Test', DEFAULT_SIZE);
expect(db).toBeDefined();
db.transaction(function(tx) {
expect(tx).toBeDefined();
tx.executeSql('CREATE TABLE TT(C);');
tx.executeSql('INSERT INTO TT VALUES (?),(?),(?),(?),(?);', [1,2,3,4,5]);
tx.executeSql('DELETE FROM TT LIMIT 3;', [], function(tx, res) {
tx.executeSql('SELECT * FROM TT;', [], function(tx, res) {
// EXPECTED RESULT:
expect(true).toBe(true);
expect(res.rows.length).toBe(2);
// Close (plugin only) & finish:
(isWebSql) ? done() : db.close(done, done);
});
}, function(tx, err) {
// NOT EXPECTED (went wrong):
expect(false).toBe(true);
expect(JSON.stringify(err)).toBe('--');
// Close (plugin only) & finish:
(isWebSql) ? done() : db.close(done, done);
});
});
}, MYTIMEOUT);
});
}
}
if (window.hasBrowser) mytests();
else exports.defineAutoTests = mytests;
/* vim: set expandtab : */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,123 @@
/* 'use strict'; */
var MYTIMEOUT = 12000;
// NOTE: DEFAULT_SIZE wanted depends on type of browser
var isWindows = /MSAppHost/.test(navigator.userAgent);
var isAndroid = !isWindows && /Android/.test(navigator.userAgent);
var isFirefox = /Firefox/.test(navigator.userAgent);
var isWebKitBrowser = !isWindows && !isAndroid && /Safari/.test(navigator.userAgent);
var isBrowser = isWebKitBrowser || isFirefox;
var isEdgeBrowser = isBrowser && (/Edge/.test(navigator.userAgent));
var isChromeBrowser = isBrowser && !isEdgeBrowser && (/Chrome/.test(navigator.userAgent));
var isSafariBrowser = isWebKitBrowser && !isEdgeBrowser && !isChromeBrowser;
// should avoid popups (Safari seems to count 2x)
var DEFAULT_SIZE = isSafariBrowser ? 2000000 : 5000000;
// FUTURE TBD: 50MB should be OK on Chrome and some other test browsers.
// NOTE: While in certain version branches there is no difference between
// the default Android implementation and implementation #2,
// this test script will also apply the androidLockWorkaround: 1 option
// in case of implementation #2.
var scenarioList = [
isAndroid ? 'Plugin-implementation-default' : 'Plugin',
'HTML5',
'Plugin-implementation-2'
];
var scenarioCount = (!!window.hasWebKitWebSQL) ? (isAndroid ? 3 : 2) : 1;
var mytests = function() {
for (var i=0; i<scenarioCount; ++i) {
describe(scenarioList[i] + ': ext tx blob test(s)', function() {
var scenarioName = scenarioList[i];
var suiteName = scenarioName + ': ';
var isWebSql = (i === 1);
var isImpl2 = (i === 2);
// NOTE: MUST be defined in function scope, NOT outer scope:
var openDatabase = function(name, ignored1, ignored2, ignored3) {
if (isImpl2) {
return window.sqlitePlugin.openDatabase({
// prevent reuse of database from default db implementation:
name: 'i2-'+name,
// explicit database location:
location: 'default',
androidDatabaseImplementation: 2,
androidLockWorkaround: 1
});
}
if (isWebSql) {
return window.openDatabase(name, '1.0', 'Test', DEFAULT_SIZE);
} else {
// explicit database location:
return window.sqlitePlugin.openDatabase({name: name, location: 'default'});
}
}
describe(scenarioList[i] + ': Blob object test(s)', function() {
// This test shows that the plugin does not throw an error when trying to serialize
// a non-standard parameter type. Blob becomes an empty dictionary on iOS, for example,
// and so this verifies the type is converted to a string and continues. Web SQL does
// the same but on the JavaScript side and converts to a string like `[object Blob]`.
it(suiteName + "INSERT Blob from ArrayBuffer (non-standard parameter type)", function(done) {
if (/Android 4.[1-3]/.test(navigator.userAgent)) pending('SKIP for Android 4.1-4.3');
// IMPORTANT:
if (typeof Blob === "undefined") pending('Blob type does not exist');
// SKIP this test if ArrayBuffer is undefined
// TODO: consider trying this for multiple non-standard parameter types instead
if (typeof ArrayBuffer === "undefined") pending('ArrayBuffer type does not exist');
var db = openDatabase('Blob-object-from-ArrayBuffer-test.db');
expect(db).toBeDefined();
db.transaction(function(tx) {
expect(tx).toBeDefined();
var buffer = new ArrayBuffer(5);
var view = new Uint8Array(buffer);
view[0] = 'h'.charCodeAt();
view[1] = 'e'.charCodeAt();
view[2] = 'l'.charCodeAt();
view[3] = 'l'.charCodeAt();
view[4] = 'o'.charCodeAt();
var blob = new Blob([view.buffer], { type:"application/octet-stream" });
tx.executeSql('DROP TABLE IF EXISTS test_table');
tx.executeSql('CREATE TABLE IF NOT EXISTS test_table (foo blob)');
tx.executeSql('INSERT INTO test_table VALUES (?)', [blob], function(txIgnored, rs) {
// EXPECTED RESULT:
expect(rs).toBeDefined();
done();
}, function(tx, error) {
// NOT EXPECTED:
expect(false).toBe(true);
expect(error.message).toBe('--');
done();
});
}, function(err) {
// NOT EXPECTED:
expect(false).toBe(true);
expect(err.message).toBe('--');
done();
});
});
});
});
}
}
if (window.hasBrowser) mytests();
else exports.defineAutoTests = mytests;
/* vim: set expandtab : */

View File

@ -0,0 +1,97 @@
/* 'use strict'; */
var MYTIMEOUT = 12000;
// NOTE: DEFAULT_SIZE wanted depends on type of browser
// Detect actual platform:
var isWindows = /MSAppHost/.test(navigator.userAgent);
var isAndroid = !isWindows && /Android/.test(navigator.userAgent);
var isFirefox = /Firefox/.test(navigator.userAgent);
var isWebKitBrowser = !isWindows && !isAndroid && /Safari/.test(navigator.userAgent);
var isBrowser = isWebKitBrowser || isFirefox;
var isEdgeBrowser = isBrowser && (/Edge/.test(navigator.userAgent));
var isChromeBrowser = isBrowser && !isEdgeBrowser && (/Chrome/.test(navigator.userAgent));
var isSafariBrowser = isWebKitBrowser && !isEdgeBrowser && !isChromeBrowser;
var isMac = !isBrowser && /Macintosh/.test(navigator.userAgent);
var isAppleMobileOS = /iPhone/.test(navigator.userAgent) ||
/iPad/.test(navigator.userAgent) || /iPod/.test(navigator.userAgent);
// should avoid popups (Safari seems to count 2x)
var DEFAULT_SIZE = isSafariBrowser ? 2000000 : 5000000;
// FUTURE TBD: 50MB should be OK on Chrome and some other test browsers.
var scenarioList = [ isAndroid ? 'Plugin-implementation-default' : 'Plugin', 'HTML5', 'Plugin-implementation-2' ];
var scenarioCount = (!!window.hasWebKitWebSQL) ? (isAndroid ? 3 : 2) : 1;
var mytests = function() {
for (var i=0; i<scenarioCount; ++i) {
describe(scenarioList[i] + ': REGEX test(s)', function() {
var scenarioName = scenarioList[i];
var suiteName = scenarioName + ': ';
var isWebSql = (i === 1);
var isImpl2 = (i === 2);
// NOTE: MUST be defined in function scope, NOT outer scope:
var openDatabase = function(name, ignored1, ignored2, ignored3) {
if (isImpl2) {
// explicit database location:
return window.sqlitePlugin.openDatabase({name: name, location: 'default', androidDatabaseImplementation: 2});
}
if (isWebSql) {
return window.openDatabase(name, "1.0", "Demo", DEFAULT_SIZE);
} else {
// explicit database location:
return window.sqlitePlugin.openDatabase({name: name, location: 'default'});
}
}
it(suiteName + 'Simple REGEXP test',
function(done) {
if (isWebSql && isBrowser && !isChromeBrowser) pending('SKIP on (WebKit) Web SQL on non-Chrome desktop browser');
if (isWebSql && /Android 4.[1-3]/.test(navigator.userAgent)) pending('SKIP for Android 4.1-4.3 (WebKit) Web SQL');
if (isWebSql && isAppleMobileOS) pending('SKIP on iOS (WebKit) Web SQL');
if (!isWebSql && isBrowser) pending('SKIP on plugin on browser - NOT IMPLEMENTED');
if (!isWebSql && isWindows) pending('SKIP on Windows plugin - NOT IMPLEMENTED');
if (!isWebSql && isAndroid && isImpl2 && /Android [2-4]/.test(navigator.userAgent)) pending('TBD SKIP for system android.database provider on Android 2.x-4.x');
// TBD REMOVE the following conditions for plugin versions such as cordova-sqlite-ext:
if (!isWebSql && isAndroid && !isImpl2) pending('SKIP on Android plugin with default database provider');
if (!isWebSql && (isAppleMobileOS || isMac)) pending('SKIP on iOS/macOS plugin');
var db = openDatabase('simple-regexp-test.db', '1.0', 'test', DEFAULT_SIZE);
expect(db).toBeDefined();
db.transaction(function(tx) {
expect(tx).toBeDefined();
tx.executeSql('DROP TABLE IF EXISTS tt');
tx.executeSql('CREATE TABLE tt (tv TEXT)');
tx.executeSql('INSERT INTO tt VALUES (?)', ['test']);
tx.executeSql('INSERT INTO tt VALUES (?)', ['tst2']);
tx.executeSql("SELECT * from tt WHERE tv REGEXP('te?st2+')", [], function(tx, res) {
expect(res.rows.length).toBe(1);
expect(res.rows.item(0).tv).toBe('tst2');
done();
}, function(e) {
// went wrong:
expect(false).toBe(true);
done();
});
});
}, MYTIMEOUT);
});
};
}
if (window.hasBrowser) mytests();
else exports.defineAutoTests = mytests;
/* vim: set expandtab : */

View File

@ -0,0 +1,52 @@
/* 'use strict'; */
var MYTIMEOUT = 12000;
var isWindows = /MSAppHost/.test(navigator.userAgent);
var isAndroid = !isWindows && /Android/.test(navigator.userAgent);
var isFirefox = /Firefox/.test(navigator.userAgent);
var isWebKitBrowser = !isWindows && !isAndroid && /Safari/.test(navigator.userAgent);
var isBrowser = isWebKitBrowser || isFirefox;
var mytests = function() {
describe('Built-in test(s)', function() {
describe('Self test(s)', function() {
it('Echo test',
function(done) {
window.sqlitePlugin.echoTest(function() {
// ok:
expect(true).toBe(true);
done();
}, function(err) {
// went wrong:
expect(false).toBe(true);
expect('Echo test error: ' + JSON.stringify(err)).toBe('--');
done();
});
}, MYTIMEOUT);
it('Self-test with CRUD operations & cleanup',
function(done) {
window.sqlitePlugin.selfTest(function() {
// ok:
expect(true).toBe(true);
done();
}, function(err) {
// went wrong:
expect(false).toBe(true);
expect('Self-test error: ' + JSON.stringify(err)).toBe('--');
done();
});
}, MYTIMEOUT);
});
});
};
if (window.hasBrowser) mytests();
else exports.defineAutoTests = mytests;
/* vim: set expandtab : */

View File

@ -0,0 +1,788 @@
/* 'use strict'; */
var MYTIMEOUT = 12000;
var isWindows = /MSAppHost/.test(navigator.userAgent);
var isAndroid = !isWindows && /Android/.test(navigator.userAgent);
var isFirefox = /Firefox/.test(navigator.userAgent);
var isWebKitBrowser = !isWindows && !isAndroid && /Safari/.test(navigator.userAgent);
var isBrowser = isWebKitBrowser || isFirefox;
var isMac = !isBrowser && /Macintosh/.test(navigator.userAgent);
var isAppleMobileOS = /iPhone/.test(navigator.userAgent) ||
/iPad/.test(navigator.userAgent) || /iPod/.test(navigator.userAgent);
var hasMobileWKWebView = isAppleMobileOS && !!window.webkit && !!window.webkit.messageHandlers;
// NOTE: While in certain version branches there is no difference between
// the default Android implementation and implementation #2,
// this test script will also apply the androidLockWorkaround: 1 option
// in case of implementation #2.
var pluginScenarioList = [
isAndroid ? 'Plugin-implementation-default' : 'Plugin',
'Plugin-implementation-2'
];
var pluginScenarioCount = isAndroid ? 2 : 1;
var mytests = function() {
for (var i=0; i<pluginScenarioCount; ++i) {
describe(pluginScenarioList[i] + ': sqlBatch test(s)', function() {
var scenarioName = pluginScenarioList[i];
var suiteName = scenarioName + ': ';
var isImpl2 = (i === 1);
// NOTE: MUST be defined in function scope, NOT outer scope:
var openDatabase = function(name, ignored1, ignored2, ignored3) {
if (isImpl2) {
return window.sqlitePlugin.openDatabase({
// prevent reuse of database from default db implementation:
name: 'i2-'+name,
// explicit database location:
location: 'default',
androidDatabaseImplementation: 2,
androidLockWorkaround: 1
});
} else {
// explicit database location:
return window.sqlitePlugin.openDatabase({name: name, location: 'default'});
}
}
describe(pluginScenarioList[i] + ': Basic sql batch test(s)', function() {
it(suiteName + 'Single-column batch sql test', function(done) {
var db = openDatabase('Single-column-batch-sql-test.db');
expect(db).toBeDefined();
db.sqlBatch([
'DROP TABLE IF EXISTS MyTable',
'CREATE TABLE MyTable (data)',
[ 'INSERT INTO MyTable VALUES (?)', ['test-value'] ],
], function() {
db.executeSql('SELECT * FROM MyTable', [], function (rs) {
expect(rs.rows).toBeDefined();
expect(rs.rows.length).toBe(1);
expect(rs.rows.item(0).data).toBe('test-value');
db.close(done, done);
});
}, function(error) {
// NOT EXPECTED:
expect(false).toBe(true);
expect(error.message).toBe('--');
db.close(done, done);
});
}, MYTIMEOUT);
it(suiteName + 'Single-column batch sql test 2 (CREATE TABLE SQL statement with no parameters in [])', function(done) {
var db = openDatabase('Single-column-batch-sql-test-2.db');
expect(db).toBeDefined();
db.sqlBatch([
'DROP TABLE IF EXISTS MyTable',
[ 'CREATE TABLE MyTable (data)' ],
[ 'INSERT INTO MyTable VALUES (?)', ['test-value'] ],
], function() {
db.executeSql('SELECT * FROM MyTable', [], function (rs) {
expect(rs.rows).toBeDefined();
expect(rs.rows.length).toBe(1);
expect(rs.rows.item(0).data).toBe('test-value');
db.close(done, done);
});
}, function(error) {
// NOT EXPECTED:
expect(false).toBe(true);
expect(error.message).toBe('--');
db.close(done, done);
});
}, MYTIMEOUT);
it(suiteName + 'Single-column batch sql test values: INSERT INTEGER/REAL number values and check stored data', function(done) {
var db = openDatabase('Single-column-batch-sql-test-number-values.db');
expect(db).toBeDefined();
db.sqlBatch([
'DROP TABLE IF EXISTS MyTable',
'CREATE TABLE MyTable (data)',
[ 'INSERT INTO MyTable VALUES (?)', [101] ],
[ 'INSERT INTO MyTable VALUES (?)', [-101] ],
[ 'INSERT INTO MyTable VALUES (?)', [123.456] ],
[ 'INSERT INTO MyTable VALUES (?)', [-123.456] ],
[ 'INSERT INTO MyTable VALUES (?)', [1234567890123] ],
[ 'INSERT INTO MyTable VALUES (?)', [-1234567890123] ],
[ 'INSERT INTO MyTable VALUES (?)', [0] ],
], function() {
db.executeSql('SELECT data AS d1, TYPEOF(data) AS t1, ABS(data) AS a1, UPPER(data) as u1 FROM MyTable', [], function (rs) {
expect(rs.rows).toBeDefined();
expect(rs.rows.length).toBe(7);
expect(rs.rows.item(0).d1).toBe(101);
if (isMac || hasMobileWKWebView)
expect(rs.rows.item(0).t1).toBe('real');
else
expect(rs.rows.item(0).t1).toBe('integer');
expect(rs.rows.item(0).a1).toBe(101);
if (isMac || hasMobileWKWebView)
expect(rs.rows.item(0).u1).toBe('101.0');
else
expect(rs.rows.item(0).u1).toBe('101');
expect(rs.rows.item(1).d1).toBe(-101);
if (isMac || hasMobileWKWebView)
expect(rs.rows.item(1).t1).toBe('real');
else
expect(rs.rows.item(1).t1).toBe('integer');
expect(rs.rows.item(1).a1).toBe(101);
if (isMac || hasMobileWKWebView)
expect(rs.rows.item(1).u1).toBe('-101.0');
else
expect(rs.rows.item(1).u1).toBe('-101');
expect(rs.rows.item(2).d1).toBe(123.456);
expect(rs.rows.item(2).t1).toBe('real');
expect(rs.rows.item(2).a1).toBe(123.456);
expect(rs.rows.item(2).u1).toBe('123.456');
expect(rs.rows.item(3).d1).toBe(-123.456);
expect(rs.rows.item(3).t1).toBe('real');
expect(rs.rows.item(3).a1).toBe(123.456);
expect(rs.rows.item(3).u1).toBe('-123.456');
expect(rs.rows.item(4).d1).toBe(1234567890123);
if (isBrowser || isMac || hasMobileWKWebView)
expect(rs.rows.item(4).t1).toBe('real');
else
expect(rs.rows.item(4).t1).toBe('integer');
expect(rs.rows.item(4).a1).toBe(1234567890123);
if (isBrowser || isMac || hasMobileWKWebView)
expect(rs.rows.item(4).u1).toBe('1234567890123.0');
else
expect(rs.rows.item(4).u1).toBe('1234567890123');
expect(rs.rows.item(5).d1).toBe(-1234567890123);
if (isBrowser || isMac || hasMobileWKWebView)
expect(rs.rows.item(5).t1).toBe('real');
else
expect(rs.rows.item(5).t1).toBe('integer');
expect(rs.rows.item(5).a1).toBe(1234567890123);
if (isBrowser || isMac || hasMobileWKWebView)
expect(rs.rows.item(5).u1).toBe('-1234567890123.0');
else
expect(rs.rows.item(5).u1).toBe('-1234567890123');
expect(rs.rows.item(6).d1).toBe(0);
if (isMac || hasMobileWKWebView)
expect(rs.rows.item(6).t1).toBe('real');
else
expect(rs.rows.item(6).t1).toBe('integer');
expect(rs.rows.item(6).a1).toBe(0);
if (isMac || hasMobileWKWebView)
expect(rs.rows.item(6).u1).toBe('0.0');
else
expect(rs.rows.item(6).u1).toBe('0');
db.close(done, done);
});
}, function(error) {
// NOT EXPECTED:
expect(false).toBe(true);
expect(error.message).toBe('--');
db.close(done, done);
});
}, MYTIMEOUT);
it(suiteName + 'Single-column batch sql test values: INSERT null/undefined values and check stored data', function(done) {
var db = openDatabase('Single-column-batch-sql-test-null-undefined-values.db');
expect(db).toBeDefined();
db.sqlBatch([
'DROP TABLE IF EXISTS MyTable',
'CREATE TABLE MyTable (data)',
[ 'INSERT INTO MyTable VALUES (?)', [null] ],
[ 'INSERT INTO MyTable VALUES (?)', [undefined] ],
], function() {
db.executeSql('SELECT data AS d1, TYPEOF(data) AS t1, ABS(data) AS a1, UPPER(data) as u1 FROM MyTable', [], function (rs) {
expect(rs.rows).toBeDefined();
expect(rs.rows.length).toBe(2);
expect(rs.rows.item(0).d1).toBe(null);
expect(rs.rows.item(0).t1).toBe('null');
expect(rs.rows.item(0).a1).toBe(null);
expect(rs.rows.item(0).u1).toBe(null);
expect(rs.rows.item(1).d1).toBe(null);
expect(rs.rows.item(1).t1).toBe('null');
expect(rs.rows.item(1).a1).toBe(null);
expect(rs.rows.item(1).u1).toBe(null);
db.close(done, done);
});
}, function(error) {
// NOT EXPECTED:
expect(false).toBe(true);
expect(error.message).toBe('--');
db.close(done, done);
});
}, MYTIMEOUT);
it(suiteName + 'Single-column batch sql test values: INSERT +/- Infinity & NaN values and check stored data [TBD Android/iOS/macOS plugin result for +/- Infinity]', function(done) {
if (isMac) pending('SKIP for macOS [CRASH]'); // FUTURE TBD
var db = openDatabase('Single-column-batch-sql-test-infinity-nan-values.db');
expect(db).toBeDefined();
db.sqlBatch([
'DROP TABLE IF EXISTS MyTable',
'CREATE TABLE MyTable (data)',
[ 'INSERT INTO MyTable VALUES (?)', [Infinity] ],
[ 'INSERT INTO MyTable VALUES (?)', [-Infinity] ],
[ 'INSERT INTO MyTable VALUES (?)', [NaN] ],
], function() {
db.executeSql('SELECT data AS d1, TYPEOF(data) AS t1, ABS(data) AS a1, UPPER(data) as u1 FROM MyTable', [], function (rs) {
expect(rs.rows).toBeDefined();
expect(rs.rows.length).toBe(3);
if (isBrowser || isWindows) {
expect(rs.rows.item(0).d1).toBe(Infinity);
expect(rs.rows.item(0).t1).toBe('real');
expect(rs.rows.item(0).a1).toBe(Infinity);
expect(rs.rows.item(0).u1).toBe('INF');
expect(rs.rows.item(1).d1).toBe(-Infinity);
expect(rs.rows.item(1).t1).toBe('real');
expect(rs.rows.item(1).a1).toBe(Infinity);
expect(rs.rows.item(1).u1).toBe('-INF');
} else {
expect(rs.rows.item(0).d1).toBe(null);
expect(rs.rows.item(0).t1).toBe('null');
expect(rs.rows.item(0).a1).toBe(null);
expect(rs.rows.item(0).u1).toBe(null);
expect(rs.rows.item(1).d1).toBe(null);
expect(rs.rows.item(1).t1).toBe('null');
expect(rs.rows.item(1).a1).toBe(null);
expect(rs.rows.item(1).u1).toBe(null);
}
expect(rs.rows.item(2).d1).toBe(null);
expect(rs.rows.item(2).t1).toBe('null');
expect(rs.rows.item(2).a1).toBe(null);
expect(rs.rows.item(2).u1).toBe(null);
db.close(done, done);
});
}, function(error) {
// NOT EXPECTED:
expect(false).toBe(true);
expect(error.message).toBe('--');
db.close(done, done);
});
}, MYTIMEOUT);
it(suiteName + 'Single-column batch sql test values: INSERT true/false values and check stored data [stored as strings]', function(done) {
var db = openDatabase('Single-column-batch-sql-test-true-false-values.db');
db.sqlBatch([
'DROP TABLE IF EXISTS MyTable',
'CREATE TABLE MyTable (data)',
[ 'INSERT INTO MyTable VALUES (?)', [true] ],
[ 'INSERT INTO MyTable VALUES (?)', [false] ],
], function() {
db.executeSql('SELECT data AS d1, TYPEOF(data) AS t1, ABS(data) AS a1, UPPER(data) as u1 FROM MyTable', [], function (rs) {
expect(rs.rows).toBeDefined();
expect(rs.rows.length).toBe(2);
expect(rs.rows.item(0).d1).toBe('true');
expect(rs.rows.item(0).t1).toBe('text');
expect(rs.rows.item(0).a1).toBe(0);
expect(rs.rows.item(0).u1).toBe('TRUE');
expect(rs.rows.item(1).d1).toBe('false');
expect(rs.rows.item(1).t1).toBe('text');
expect(rs.rows.item(1).a1).toBe(0);
expect(rs.rows.item(1).u1).toBe('FALSE');
db.close(done, done);
});
}, function(error) {
// NOT EXPECTED:
expect(false).toBe(true);
expect(error.message).toBe('--');
db.close(done, done);
});
}, MYTIMEOUT);
it(suiteName + 'sql batch with changing SQL element [TBD POSSIBLY INCONSISTENT BEHAVIOR]', function(done) {
var db = openDatabase('sql-batch-with-changing-sql-test.db');
var mybatch = [
'DROP TABLE IF EXISTS MyTable',
'CREATE TABLE MyTable (data)',
"INSERT INTO MyTable VALUES ('Alice')"
];
db.sqlBatch(mybatch, function() {
db.executeSql('SELECT * FROM MyTable', [], function (rs) {
expect(rs.rows.length).toBe(1);
expect(rs.rows.item(0).data).toBe('Alice');
db.close(done, done);
});
}, function(error) {
// NOT EXPECTED:
expect(false).toBe(true);
expect(error.message).toBe('--');
db.close(done, done);
});
mybatch[2] = "INSERT INTO MyTable VALUES ('Betty')";
}, MYTIMEOUT);
it(suiteName + 'sql batch with changing argument value [TBD POSSIBLY INCONSISTENT BEHAVIOR]', function(done) {
var db = openDatabase('sql-batch-with-changing-argument-value-test.db');
var mybatch = [
'DROP TABLE IF EXISTS MyTable',
'CREATE TABLE MyTable (data)',
['INSERT INTO MyTable VALUES (?)', ['Alice']]
];
db.sqlBatch(mybatch, function() {
db.executeSql('SELECT * FROM MyTable', [], function (rs) {
expect(rs.rows.length).toBe(1);
expect(rs.rows.item(0).data).toBe('Betty');
db.close(done, done);
});
}, function(error) {
// NOT EXPECTED:
expect(false).toBe(true);
expect(error.message).toBe('--');
db.close(done, done);
});
mybatch[2][1][0] = 'Betty';
}, MYTIMEOUT);
it(suiteName + 'batch sql with dynamic object for SQL [INCONSISTENT BEHAVIOR]', function(done) {
// MyDynamicObject "class":
function MyDynamicObject() { this.name = 'Alice'; };
MyDynamicObject.prototype.toString = function() {return "INSERT INTO MyTable VALUES ('" + this.name + "')";}
var myObject = new MyDynamicObject();
// Check myObject:
expect(myObject.toString()).toBe("INSERT INTO MyTable VALUES ('Alice')");
var db = openDatabase('batch-sql-with-dynamic-object-for-sql.db');
expect(db).toBeDefined();
myObject.name = 'Betty';
db.sqlBatch([
'DROP TABLE IF EXISTS MyTable',
'CREATE TABLE MyTable (data)',
myObject
], function() {
db.executeSql('SELECT * FROM MyTable', [], function (res) {
expect(res.rows.item(0).data).toBe('Carol');
db.close(done, done);
});
}, function(error) {
// NOT EXPECTED:
expect(false).toBe(true);
expect(error.message).toBe('--');
db.close(done, done);
});
myObject.name = 'Carol';
}, MYTIMEOUT);
it(suiteName + 'batch sql with dynamic object for SQL arg value [INCONSISTENT BEHAVIOR]', function(done) {
// MyDynamicParameterObject "class":
function MyDynamicParameterObject() {this.name='Alice';};
MyDynamicParameterObject.prototype.toString = function() {return this.name;};
var myObject = new MyDynamicParameterObject();
// Check myObject:
expect(myObject.toString()).toBe('Alice');
var db = openDatabase('batch-sql-with-dynamic-object-for-sql-arg-value.db');
expect(db).toBeDefined();
myObject.name = 'Betty';
db.sqlBatch([
'DROP TABLE IF EXISTS MyTable',
'CREATE TABLE MyTable (data)',
[ 'INSERT INTO MyTable VALUES (?)', [myObject] ],
], function() {
db.executeSql('SELECT * FROM MyTable', [], function (res) {
expect(res.rows.item(0).data).toBe('Carol');
db.close(done, done);
});
}, function(error) {
// NOT EXPECTED:
expect(false).toBe(true);
expect(error.message).toBe('--');
db.close(done, done);
});
myObject.name = 'Carol';
}, MYTIMEOUT);
it(suiteName + 'Multi-row INSERT with parameters in batch sql test', function(done) {
var db = openDatabase('Multi-row-INSERT-with-parameters-batch-sql-test.db');
expect(db).toBeDefined();
db.sqlBatch([
'DROP TABLE IF EXISTS MyTable',
'CREATE TABLE MyTable (x,y)',
[ 'INSERT INTO MyTable VALUES (?,?),(?,?)', ['a',1,'b',2] ],
], function() {
db.executeSql('SELECT * FROM MyTable', [], function (resultSet) {
// EXPECTED: CORRECT RESULT:
expect(resultSet.rows.length).toBe(2);
expect(resultSet.rows.item(0).x).toBe('a');
expect(resultSet.rows.item(0).y).toBe(1);
expect(resultSet.rows.item(1).x).toBe('b');
expect(resultSet.rows.item(1).y).toBe(2);
db.close(done, done);
});
}, function(error) {
// NOT EXPECTED:
expect(false).toBe(true);
expect(error.message).toBe('--');
db.close(done, done);
});
}, MYTIMEOUT);
it(suiteName + 'sqlBatch INSERT with numbered parameters (reversed)', function(done) {
var db = openDatabase('sqlBatch-INSERT-with-numbered-parameters-reversed-test.db');
expect(db).toBeDefined();
db.sqlBatch([
'DROP TABLE IF EXISTS MyTable',
'CREATE TABLE MyTable (x,y)',
[ 'INSERT INTO MyTable VALUES (?2,?1)', ['a',1] ],
], function() {
db.executeSql('SELECT * FROM MyTable', [], function (resultSet) {
// EXPECTED: CORRECT RESULT:
expect(resultSet.rows.length).toBe(1);
expect(resultSet.rows.item(0).x).toBe(1);
expect(resultSet.rows.item(0).y).toBe('a');
db.close(done, done);
});
}, function(error) {
// NOT EXPECTED:
expect(false).toBe(true);
expect(error.message).toBe('--');
db.close(done, done);
});
}, MYTIMEOUT);
it(suiteName + 'batch sql with syntax error', function(done) {
var db = openDatabase('batch-sql-syntax-error-test.db');
expect(db).toBeDefined();
db.sqlBatch([
'DROP TABLE IF EXISTS MyTable',
// syntax error below:
'CRETE TABLE MyTable (SampleColumn)',
[ 'INSERT INTO MyTable VALUES (?)', ['test-value'] ],
], function() {
// NOT EXPECTED:
expect(true).toBe(false);
db.close(done, done);
}, function(error) {
// EXPECTED RESULT:
expect(error).toBeDefined();
expect(error.code).toBeDefined();
expect(error.message).toBeDefined();
if (isBrowser || isWindows || (isAndroid && isImpl2))
expect(error.code).toBe(0);
else
expect(error.code).toBe(5);
if (isWindows)
expect(error.message).toMatch(/a statement with no error handler failed: Error preparing an SQLite statement/);
else
expect(error.message).toMatch(/a statement with no error handler failed.*near \"CRETE\": syntax error/);
db.close(done, done);
});
}, MYTIMEOUT);
it(suiteName + 'batch sql with constraint violation (check error code & basic error message pattern)', function(done) {
var db = openDatabase('batch-sql-constraint-violation-test.db');
expect(db).toBeDefined();
db.sqlBatch([
'DROP TABLE IF EXISTS MyTable',
// syntax error below:
'CREATE TABLE MyTable (data UNIQUE)',
[ 'INSERT INTO MyTable VALUES (?)', [123] ],
[ 'INSERT INTO MyTable VALUES (?)', [123] ],
], function() {
// NOT EXPECTED:
expect(false).toBe(true);
db.close(done, done);
}, function(error) {
// EXPECTED RESULT:
expect(error).toBeDefined();
expect(error.code).toBeDefined();
expect(error.message).toBeDefined();
if (isBrowser || isWindows)
expect(error.code).toBe(0);
else
expect(error.code).toBe(6);
if (isWindows)
expect(error.message).toMatch(/a statement with no error handler failed: SQLite3 step error result code: 1/);
else
expect(error.message).toMatch(/a statement with no error handler failed.*constraint fail/);
db.close(done, done);
});
}, MYTIMEOUT);
it(suiteName + 'batch sql failure-safe semantics', function(done) {
var db = openDatabase('batch-sql-failure-safe-test.db');
expect(db).toBeDefined();
db.executeSql('DROP TABLE IF EXISTS MyTable');
db.executeSql('CREATE TABLE MyTable (SampleColumn)');
db.executeSql('INSERT INTO MyTable VALUES (?)', ['test-value'], function() {
db.sqlBatch([
'DELETE FROM MyTable',
// syntax error below:
[ 'INSRT INTO MyTable VALUES (?)', 'test-value' ]
], function() {
// NOT EXPECTED:
expect(true).toBe(false);
db.close(done, done);
}, function(error) {
// CHECK INTEGRITY & FINISH:
db.executeSql('SELECT * FROM MyTable', [], function (res) {
expect(res.rows.item(0).SampleColumn).toBe('test-value');
db.close(done, done);
});
});
}, function(error) {
// NOT EXPECTED:
expect(false).toBe(true);
expect(error.message).toBe('--');
db.close(done, done);
});
}, MYTIMEOUT);
it(suiteName + 'sqlBatch() with no arguments (BOGUS)', function(done) {
var db = openDatabase('sql-batch-with-no-arguments.db');
try {
db.sqlBatch();
// SHOULD NOT GET HERE:
expect(false).toBe(true);
} catch(e) {
// EXPECTED:
expect(e).toBeDefined();
expect(e.message).toMatch(/sqlBatch expects an array/);
db.close(done, done);
};
}, MYTIMEOUT);
it(suiteName + 'sqlBatch([]) (empty array) - reports success', function(done) {
var db = openDatabase('sql-batch-with-empty-array-argument-test.db');
expect(db).toBeDefined();
try {
db.sqlBatch([], function() {
// EXPECTED RESULT:
db.close(done, done);
}, function(error) {
// NOT EXPECTED:
expect(false).toBe(true);
expect(error).toBeDefined();
expect(error.message).toBeDefined();
expect(error.message).toBe('--');
db.close(done, done);
});
} catch(e) {
// NOT EXPECTED:
expect(false).toBe(true);
expect(e).toBeDefined();
expect(e.message).toBeDefined();
expect(e.message).toBe('--');
db.close(done, done);
};
}, MYTIMEOUT);
it(suiteName + 'sqlBatch with [] for sql batch item (BOGUS)', function(done) {
var db = openDatabase('sql-batch-with-empty-array-for-batch-item.db');
try {
db.sqlBatch(['SELECT 1', []], function() {
// NOT EXPECTED:
expect(false).toBe(true);
db.close(done, done);
}, function(error) {
// NOT EXPECTED:
expect(false).toBe(true);
db.close(done, done);
});
// SHOULD NOT GET HERE:
expect(false).toBe(true);
} catch(e) {
expect(e).toBeDefined();
expect(e.message).toMatch(/sqlBatch array element of zero .*0.* length/);
db.close(done, done);
};
}, MYTIMEOUT);
it(suiteName + 'sqlBatch with true for SQL statements (BOGUS)', function(done) {
var db = openDatabase('sql-batch-with-true-for-sql-statements.db');
try {
db.sqlBatch(true, function() {
// NOT EXPECTED:
expect(false).toBe(true);
db.close(done, done);
}, function(error) {
// NOT EXPECTED:
expect(false).toBe(true);
db.close(done, done);
});
// SHOULD NOT GET HERE:
expect(false).toBe(true);
} catch(e) {
expect(e).toBeDefined();
expect(e.message).toMatch(/sqlBatch expects an array/);
db.close(done, done);
};
}, MYTIMEOUT);
it(suiteName + 'batch sql with batch item with false for arguments array (BOGUS)', function(done) {
var db = openDatabase('batch-sql-with-false-for-args-array.db');
var check1 = false;
try {
db.sqlBatch([
'SELECT 1',
[ 'SELECT 1', false ],
], function() {
// TBD EXPECTED RESULT:
check1 = true;
}, function(error) {
// TBD NOT EXPECTED:
expect(false).toBe(true);
expect(error.message).toBe('--');
});
} catch(e) {
expect('Plugin behavior changed please update this test').toBe('--');
db.close(done, done);
};
db.sqlBatch([
'SELECT 1',
], function() {
// EXPECTED RESULT:
expect(check1).toBe(true);
db.close(done, done);
}, function(error) {
// NOT EXPECTED:
expect(false).toBe(true);
expect(error.message).toBe('--');
db.close(done, done);
});
}, MYTIMEOUT);
it(suiteName + 'batch sql with batch item with "string-value" for arguments array (BOGUS)', function(done) {
var db = openDatabase('batch-sql-with-false-for-args-array.db');
var check1 = false;
try {
db.sqlBatch([
'SELECT 1',
[ 'SELECT 1', 'string-value' ],
], function() {
// TBD EXPECTED RESULT:
check1 = true;
}, function(error) {
// TBD NOT EXPECTED:
expect(false).toBe(true);
expect(error.message).toBe('--');
});
} catch(e) {
expect('Plugin behavior changed please update this test').toBe('--');
db.close(done, done);
};
db.sqlBatch([
'SELECT 1',
], function() {
// EXPECTED RESULT:
expect(check1).toBe(true);
db.close(done, done);
}, function(error) {
// NOT EXPECTED:
expect(false).toBe(true);
expect(error.message).toBe('--');
db.close(done, done);
});
}, MYTIMEOUT);
it(suiteName + 'sqlBatch with single SELECT statement, false for error callback (BOGUS)', function(done) {
var db = openDatabase('sql-batch-with-select-false-for-error-cb.db');
try {
db.sqlBatch(['SELECT 1'], function() {
// EXPECTED:
expect(true).toBe(true);
db.close(done, done);
}, false);
} catch(e) {
expect('Plugin behavior changed please update this test').toBe('--');
db.close(done, done);
};
}, MYTIMEOUT);
it(suiteName + 'sqlBatch with single SELECT statement, string-value for error callback (BOGUS)', function(done) {
var db = openDatabase('sql-batch-with-select-string-value-for-error-cb.db');
try {
db.sqlBatch(['SELECT 1'], function() {
// EXPECTED:
expect(true).toBe(true);
db.close(done, done);
}, 'stirng-value');
} catch(e) {
expect('Plugin behavior changed please update this test').toBe('--');
db.close(done, done);
};
}, MYTIMEOUT);
it(suiteName + 'sqlBatch with error, false for success callback (BOGUS)', function(done) {
var db = openDatabase('sql-batch-with-select-false-for-success-cb.db');
try {
db.sqlBatch(['SLCT 1'], false, function(e) {
// EXPECTED:
expect(e).toBeDefined();
// TBD ...
db.close(done, done);
}, true);
} catch(e) {
expect('Plugin behavior changed please update this test').toBe('--');
db.close(done, done);
};
}, MYTIMEOUT);
it(suiteName + 'sqlBatch with error, string-value for success callback (BOGUS)', function(done) {
var db = openDatabase('sql-batch-with-select-string-value-for-success-cb.db');
try {
db.sqlBatch(['SLCT 1'], 'string-value', function(e) {
// EXPECTED:
expect(e).toBeDefined();
// TBD ...
db.close(done, done);
}, true);
} catch(e) {
expect('Plugin behavior changed please update this test').toBe('--');
db.close(done, done);
};
}, MYTIMEOUT);
});
});
}
}
if (window.hasBrowser) mytests();
else exports.defineAutoTests = mytests;
/* vim: set expandtab : */

View File

@ -0,0 +1,253 @@
/* 'use strict'; */
var MYTIMEOUT = 12000;
// NOTE: DEFAULT_SIZE wanted depends on type of browser
var isWindows = /MSAppHost/.test(navigator.userAgent);
var isAndroid = !isWindows && /Android/.test(navigator.userAgent);
var isFirefox = /Firefox/.test(navigator.userAgent);
var isWebKitBrowser = !isWindows && !isAndroid && /Safari/.test(navigator.userAgent);
var isBrowser = isWebKitBrowser || isFirefox;
var isEdgeBrowser = isBrowser && (/Edge/.test(navigator.userAgent));
var isChromeBrowser = isBrowser && !isEdgeBrowser && (/Chrome/.test(navigator.userAgent));
var isSafariBrowser = isWebKitBrowser && !isEdgeBrowser && !isChromeBrowser;
// should avoid popups (Safari seems to count 2x)
var DEFAULT_SIZE = isSafariBrowser ? 2000000 : 5000000;
// FUTURE TBD: 50MB should be OK on Chrome and some other test browsers.
// The following openDatabase settings are used for Plugin-implementation-2
// on Android:
// - androidDatabaseImplementation: 2
// - androidLockWorkaround: 1
var scenarioList = [
isAndroid ? 'Plugin-implementation-default' : 'Plugin',
'HTML5',
'Plugin-implementation-2'
];
var scenarioCount = (!!window.hasWebKitWebSQL) ? (isAndroid ? 3 : 2) : 1;
var mytests = function() {
for (var i=0; i<scenarioCount; ++i) {
describe(scenarioList[i] + ': sqlite version test(s)', function() {
var scenarioName = scenarioList[i];
var suiteName = scenarioName + ': ';
var isWebSql = (i === 1);
var isImpl2 = (i === 2);
// NOTE 1: MUST be defined in proper describe function scope, NOT outer scope.
// NOTE 2: Using same database name in this script to avoid creating extra,
// unneeded database files.
var openDatabase = function(name_ignored, ignored1, ignored2, ignored3) {
var name = 'sqlite-version-test.db';
if (isImpl2) {
return window.sqlitePlugin.openDatabase({
// prevent reuse of database from default db implementation:
name: 'i2-'+name,
androidDatabaseImplementation: 2,
androidLockWorkaround: 1,
location: 'default'
});
}
if (isWebSql) {
return window.openDatabase(name, "1.0", "Demo", DEFAULT_SIZE);
} else {
return window.sqlitePlugin.openDatabase({name: name, location: 'default'});
}
}
describe(suiteName + 'basic sqlite version test(s)', function() {
it(suiteName + 'Check sqlite version correctly matches pattern', function(done) {
var db = openDatabase("check-sqlite-version-matches-pattern.db", "1.0", "Demo", DEFAULT_SIZE);
expect(db).toBeDefined();
db.transaction(function(tx) {
expect(tx).toBeDefined();
tx.executeSql('SELECT SQLITE_VERSION() AS myResult', [], function(tx_ignored, rs) {
expect(rs).toBeDefined();
expect(rs.rows).toBeDefined();
expect(rs.rows.length).toBe(1);
expect(rs.rows.item(0).myResult).toMatch(/3\.[0-9]+\.[0-9]+/);
// Close (plugin only) & finish:
(isWebSql) ? done() : db.close(done, done);
});
}, function(error) {
// NOT EXPECTED:
expect(false).toBe(true);
expect(error.message).toBe('--');
done();
});
}, MYTIMEOUT);
it(suiteName + 'Check actual sqlite version', function(done) {
if (isWebSql) pending('NOT DETERMINISTIC for (WebKit) Web SQL');
if (!isWebSql && isAndroid && isImpl2) pending('NOT DETERMINISTIC for plugin on Android with androidDatabaseImplementation: 2');
var db = openDatabase("check-actual-sqlite-version.db", "1.0", "Demo", DEFAULT_SIZE);
expect(db).toBeDefined();
db.transaction(function(tx) {
expect(tx).toBeDefined();
tx.executeSql('SELECT SQLITE_VERSION() AS myResult', [], function(tx_ignored, rs) {
expect(rs).toBeDefined();
expect(rs.rows).toBeDefined();
expect(rs.rows.length).toBe(1);
expect(rs.rows.item(0).myResult).toBe('3.40.0');
// Close (plugin only) & finish:
(isWebSql) ? done() : db.close(done, done);
});
}, function(error) {
// NOT EXPECTED:
expect(false).toBe(true);
expect(error.message).toBe('--');
done();
});
}, MYTIMEOUT);
});
describe(suiteName + 'sqlite encoding test(s)', function() {
it(suiteName + 'Check internal database encoding: UTF-16le for Windows, UTF-8 for others (plugin ONLY)', function(done) {
if (isWebSql) pending('SKIP: NOT SUPPORTED for (WebKit) Web SQL');
var db = openDatabase("Check-sqlite-PRAGMA-encoding.db", "1.0", "Demo", DEFAULT_SIZE);
expect(db).toBeDefined();
db.executeSql('PRAGMA encoding', [], function(rs) {
expect(rs).toBeDefined();
expect(rs.rows).toBeDefined();
expect(rs.rows.length).toBe(1);
if (isWindows)
expect(rs.rows.item(0).encoding).toBe('UTF-16le');
else
expect(rs.rows.item(0).encoding).toBe('UTF-8');
// Close (plugin only) & finish:
(isWebSql) ? done() : db.close(done, done);
}, function(error) {
// NOT EXPECTED:
expect(false).toBe(true);
expect(error.message).toBe('--');
done();
});
}, MYTIMEOUT);
});
describe(suiteName + 'default page/cache size check(s)', function() {
it(suiteName + 'Check default page size (plugin ONLY)', function(done) {
// ref: litehelpers/Cordova-sqlite-storage#781
if (isWebSql) pending('SKIP: NOT SUPPORTED for (WebKit) Web SQL');
var db = openDatabase('default-page-size.db');
db.executeSql('PRAGMA page_size', null, function(rs) {
expect(rs).toBeDefined();
expect(rs.rows).toBeDefined();
expect(rs.rows.length).toBe(1);
expect(rs.rows.item(0).page_size).toBe(4096);
// Close (plugin only) & finish:
(isWebSql) ? done() : db.close(done, done);
}, function(error) {
// NOT EXPECTED:
expect(false).toBe(true);
expect(error.message).toBe('--');
done();
});
}, MYTIMEOUT);
it(suiteName + 'Check default cache size (plugin ONLY)', function(done) {
// ref: litehelpers/Cordova-sqlite-storage#781
if (isWebSql) pending('SKIP: NOT SUPPORTED for (WebKit) Web SQL');
var db = openDatabase('default-cache-size.db');
db.executeSql('PRAGMA cache_size', null, function(rs) {
expect(rs).toBeDefined();
expect(rs.rows).toBeDefined();
expect(rs.rows.length).toBe(1);
var resultRow = rs.rows.item(0);
expect(resultRow).toBeDefined();
expect(resultRow.cache_size).toBeDefined();
if (!isWebSql && isAndroid && isImpl2
&& (/Android [3-7]/.test(navigator.userAgent)))
expect(resultRow.cache_size).toBe(2000); // TBD OLD VALUE on Android (...)
else
expect(resultRow.cache_size).toBe(-2000); // NEW VALUE, otherwise
// Close (plugin only) & finish:
(isWebSql) ? done() : db.close(done, done);
}, function(error) {
// NOT EXPECTED:
expect(false).toBe(true);
expect(error.message).toBe('--');
done();
});
}, MYTIMEOUT);
});
describe(suiteName + 'additional sqlite check(s)', function() {
it(suiteName + 'Check default PRAGMA journal_mode setting (plugin ONLY)', function(done) {
if (isWebSql) pending('SKIP: NOT SUPPORTED for (WebKit) Web SQL');
var db = openDatabase("Check-sqlite-PRAGMA-encoding.db", "1.0", "Demo", DEFAULT_SIZE);
expect(db).toBeDefined();
db.executeSql('PRAGMA journal_mode', [], function(rs) {
expect(rs).toBeDefined();
expect(rs.rows).toBeDefined();
expect(rs.rows.length).toBe(1);
// DEFAULT PRAGMA journal_mode setting is
// DIFFERENT for builtin android.database implementation:
if (!isWindows && isAndroid && isImpl2)
expect(rs.rows.item(0).journal_mode).toBe(
(/Android 1/.test(navigator.userAgent)) ? 'truncate' :
(/Android 9/.test(navigator.userAgent)) ? 'wal' :
(/Android 8.1.99/.test(navigator.userAgent)) ? 'wal' :
(/Android 8/.test(navigator.userAgent)) ? 'truncate' :
'persist');
else
expect(rs.rows.item(0).journal_mode).toBe('delete');
// Close (plugin only) & finish:
(isWebSql) ? done() : db.close(done, done);
}, function(error) {
// NOT EXPECTED:
expect(false).toBe(true);
expect(error.message).toBe('--');
done();
});
}, MYTIMEOUT);
});
});
}
}
if (window.hasBrowser) mytests();
else exports.defineAutoTests = mytests;
/* vim: set expandtab : */

View File

@ -0,0 +1,814 @@
/* 'use strict'; */
var MYTIMEOUT = 12000;
// NOTE: DEFAULT_SIZE wanted depends on type of browser
var isWindows = /MSAppHost/.test(navigator.userAgent);
var isAndroid = !isWindows && /Android/.test(navigator.userAgent);
var isFirefox = /Firefox/.test(navigator.userAgent);
var isWebKitBrowser = !isWindows && !isAndroid && /Safari/.test(navigator.userAgent);
var isBrowser = isWebKitBrowser || isFirefox;
var isEdgeBrowser = isBrowser && (/Edge/.test(navigator.userAgent));
var isChromeBrowser = isBrowser && !isEdgeBrowser && (/Chrome/.test(navigator.userAgent));
var isSafariBrowser = isWebKitBrowser && !isEdgeBrowser && !isChromeBrowser;
// should avoid popups (Safari seems to count 2x)
var DEFAULT_SIZE = isSafariBrowser ? 2000000 : 5000000;
// FUTURE TBD: 50MB should be OK on Chrome and some other test browsers.
// NOTE: While in certain version branches there is no difference between
// the default Android implementation and system database provider,
// this test script will also apply the androidLockWorkaround: 1 option
// in case of androidDatabaseProvider: 'system'.
var scenarioList = [
isAndroid ? 'Plugin-implementation-default' : 'Plugin',
'HTML5',
'Plugin-system-database-provider'
];
var scenarioCount = (!!window.hasWebKitWebSQL) ? (isAndroid ? 3 : 2) : 1;
function logSuccess(message) { console.log('OK - ' + message); }
function logFailure(message) { console.log('FAILED - ' + message); }
var mytests = function() {
for (var i=0; i<scenarioCount; ++i) {
describe(scenarioList[i] + ': tx semantics test(s)', function() {
var scenarioName = scenarioList[i];
var suiteName = scenarioName + ': ';
var isWebSql = (i === 1);
var isSystemDatabaseProvider = (i === 2);
// NOTE: MUST be defined in function scope, NOT outer scope:
var openDatabase = function(name, ignored1, ignored2, ignored3) {
if (isSystemDatabaseProvider) {
return window.sqlitePlugin.openDatabase({
// prevent reuse of database from default db implementation:
name: 'system-'+name,
// explicit database location:
location: 'default',
androidDatabaseProvider: 'system',
androidLockWorkaround: 1
});
}
if (isWebSql) {
return window.openDatabase(name, '1.0', 'Test', DEFAULT_SIZE);
} else {
// explicit database location:
return window.sqlitePlugin.openDatabase({name: name, location: 'default'});
}
}
it(suiteName + 'Simple tx sql order test', function(done) {
// This test shows that executeSql statements run in intermediate callback
// are executed AFTER executeSql statements that were queued before
var db = openDatabase('Simple-tx-order-test.db', '1.0', 'Test', DEFAULT_SIZE);
expect(db).toBeDefined();
db.transaction(function(tx) {
expect(tx).toBeDefined();
tx.executeSql('DROP TABLE IF EXISTS tt');
tx.executeSql('CREATE TABLE tt (data)');
tx.executeSql('INSERT INTO tt VALUES (?)', ['first'], function(tx, res) {
expect(res).toBeDefined();
expect(res.insertId).toBeDefined();
expect(res.rowsAffected).toBe(1);
tx.executeSql('INSERT INTO tt VALUES (?)', ['middle']);
});
tx.executeSql("INSERT INTO tt VALUES ('last')");
}, null, function() {
db.transaction(function(tx) {
tx.executeSql('SELECT * FROM tt', [], function(tx, res) {
expect(res).toBeDefined();
expect(res.rows).toBeDefined();
expect(res.rows.length).toBe(3);
expect(res.rows.item(0).data).toBe('first');
expect(res.rows.item(1).data).toBe('last');
expect(res.rows.item(2).data).toBe('middle');
done();
});
});
});
}, MYTIMEOUT);
it(suiteName + 'Simple tx sql order test with error recovery', function(done) {
// This test shows that executeSql statements run in intermediate error handling callback
// are executed _after_ executeSql statements that were queued before
var db = openDatabase('tx-order-with-error-test.db', '1.0', 'Test', DEFAULT_SIZE);
expect(db).toBeDefined();
db.transaction(function(tx) {
expect(tx).toBeDefined();
tx.executeSql('DROP TABLE IF EXISTS tt');
tx.executeSql('CREATE TABLE tt (data)');
tx.executeSql('INSERT INTO tt VALUES (?)', [1], function(tx, res) {
expect(res).toBeDefined();
expect(res.insertId).toBeDefined();
expect(res.rowsAffected).toBe(1);
tx.executeSql('INSERT INTO tt VALUES (?)', [2]);
});
// syntax error:
tx.executeSql('INSRT INTO tt VALUES (?)', ['bogus'], null, function(err) {
expect(err).toBeDefined();
// TBD check err
tx.executeSql('INSERT INTO tt VALUES (?)', [3]);
return false;
});
tx.executeSql('INSERT INTO tt VALUES (?)', [4]);
}, function(err) {
// not expected:
expect(false).toBe(true);
done();
}, function() {
db.transaction(function(tx) {
tx.executeSql('SELECT * FROM tt', [], function(tx, res) {
expect(res).toBeDefined();
expect(res.rows).toBeDefined();
expect(res.rows.length).toBe(4);
expect(res.rows.item(0).data).toBe(1);
expect(res.rows.item(1).data).toBe(4);
expect(res.rows.item(2).data).toBe(2);
expect(res.rows.item(3).data).toBe(3);
done();
});
});
});
}, MYTIMEOUT);
it(suiteName + 'transaction test: check rowsAffected [intermediate]', function (done) {
var db = openDatabase("RowsAffected", "1.0", "Demo", DEFAULT_SIZE);
function test1(tx) {
tx.executeSql('DROP TABLE IF EXISTS characters');
tx.executeSql('CREATE TABLE IF NOT EXISTS characters (name, creator, fav tinyint(1))');
tx.executeSql('UPDATE characters SET name = ?', ['foo'], function (tx, res) {
expect(res.rowsAffected).toBe(0); // nothing updated
tx.executeSql('DELETE from characters WHERE name = ?', ['foo'], function (tx, res) {
expect(res.rowsAffected).toBe(0); // nothing deleted
tx.executeSql('UPDATE characters SET name = ?', ['foo'], function (tx, res) {
expect(res.rowsAffected).toBe(0); // nothing updated
tx.executeSql('DELETE from characters', [], function (tx, res) {
expect(res.rowsAffected).toBe(0); // nothing deleted
test2(tx);
});
});
});
});
}
function test2(tx) {
tx.executeSql('INSERT INTO characters VALUES (?,?,?)', ['Sonic', 'Sega', 0], function (tx, res) {
expect(res.rowsAffected).toBe(1);
tx.executeSql('INSERT INTO characters VALUES (?,?,?)', ['Mario', 'Nintendo', 0], function (tx, res) {
expect(res.rowsAffected).toBe(1);
tx.executeSql('INSERT INTO characters VALUES (?,?,?)', ['Samus', 'Nintendo', 0], function (tx, res) {
expect(res.rowsAffected).toBe(1);
tx.executeSql('UPDATE characters SET fav=1 WHERE creator=?', ['Nintendo'], function (tx, res) {
expect(res.rowsAffected).toBe(2);
tx.executeSql('UPDATE characters SET fav=1 WHERE creator=?', ['Konami'], function (tx, res) {
expect(res.rowsAffected).toBe(0);
tx.executeSql('UPDATE characters SET fav=1', [], function (tx, res) {
expect(res.rowsAffected).toBe(3);
test3(tx);
});
});
});
});
});
});
}
function test3(tx) {
tx.executeSql('INSERT INTO characters VALUES (?,?,?)', ['Mega Man', 'Capcom', 0], function (tx, res) {
expect(res.rowsAffected).toBe(1);
tx.executeSql('UPDATE characters SET fav=?, name=? WHERE creator=?;', [1, 'X', 'Capcom'], function (tx, res) {
expect(res.rowsAffected).toBe(1);
tx.executeSql('UPDATE characters SET fav=? WHERE (creator=? OR creator=?)', [1, 'Capcom', 'Nintendo'], function (tx, res) {
expect(res.rowsAffected).toBe(3);
tx.executeSql('DELETE FROM characters WHERE name="Samus";', [], function (tx, res) {
expect(res.rowsAffected).toBe(1);
tx.executeSql('UPDATE characters SET fav=0,name=?', ["foo"], function (tx, res) {
expect(res.rowsAffected).toBe(3);
tx.executeSql('DELETE FROM characters', [], function (tx, res) {
expect(res.rowsAffected).toBe(3);
done();
});
});
});
});
});
});
}
db.transaction(function (tx) {
test1(tx);
})
});
it(suiteName + 'test insertId & rowsAffected [advanced] - plugin vs (WebKit) Web SQL', function (done) {
var db = openDatabase('test-rowsAffected-advanced.db');
db.transaction(function (tx) {
tx.executeSql('DROP TABLE IF EXISTS characters');
tx.executeSql('CREATE TABLE IF NOT EXISTS characters (name unique, creator, fav tinyint(1))');
tx.executeSql('DROP TABLE IF EXISTS companies');
tx.executeSql('CREATE TABLE IF NOT EXISTS companies (name unique, fav tinyint(1))');
// INSERT or IGNORE with the real thing:
tx.executeSql('INSERT or IGNORE INTO characters VALUES (?,?,?)', ['Sonic', 'Sega', 0], function (txIgnored, rs1) {
expect(rs1.rowsAffected).toBe(1);
expect(rs1.insertId).toBe(1);
tx.executeSql('INSERT INTO characters VALUES (?,?,?)', ['Tails', 'Sega', 0], function (txIgnored, rs2) {
expect(rs2.rowsAffected).toBe(1);
expect(rs2.insertId).toBe(2);
tx.executeSql('INSERT INTO companies VALUES (?,?)', ['Sega', 1], function (txIgnored, rs3) {
expect(rs3.rowsAffected).toBe(1);
expect(rs3.insertId).toBe(1);
// query with subquery
var sql = 'UPDATE characters ' +
' SET fav=(SELECT fav FROM companies WHERE name=?)' +
' WHERE creator=?';
tx.executeSql(sql, ['Sega', 'Sega'], function (txIgnored, rs4) {
expect(rs4.rowsAffected).toBe(2);
try {
// defined on plugin (except for Android with androidDatabaseImplementation: 2);
// throws on (WebKit) Web SQL:
if (!isWebSql && isAndroid && isSystemDatabaseProvider)
expect(rs4.insertId).not.toBeDefined();
else
expect(rs4.insertId).toBeDefined();
// NOT EXPECTED to get here on (WebKit) Web SQL:
if (isWebSql) expect('(WebKit) Web SQL behavior changed').toBe('--');
if (!(isAndroid && isSystemDatabaseProvider))
expect(rs4.insertId).toBe(1);
} catch(ex) {
// SHOULD NOT CATCH EXCEPTION on plugin:
if (!isWebSql) expect('EXCEPTION NOT EXPECTED on plugin with message: ' + ex.message).toBe('--');
expect(ex).toBeDefined();
expect(ex.message).toBeDefined();
// FUTURE TBD check message
}
// query with 2 subqueries
var sql = 'UPDATE characters ' +
' SET fav=(SELECT fav FROM companies WHERE name=?),' +
' creator=(SELECT name FROM companies WHERE name=?)' +
' WHERE creator=?';
tx.executeSql(sql, ['Sega', 'Sega', 'Sega'], function (txIgnored, rs5) {
expect(rs5.rowsAffected).toBe(2);
try {
// defined on plugin (except for Android with androidDatabaseImplementation: 2);
// throws on (WebKit) Web SQL:
if (!isWebSql && isAndroid && isSystemDatabaseProvider)
expect(rs5.insertId).not.toBeDefined();
else
expect(rs5.insertId).toBeDefined();
// EXPECTED to get here on plugin only:
if (isWebSql) expect('(WebKit) Web SQL behavior changed').toBe('--');
if (!(isAndroid && isSystemDatabaseProvider))
expect(rs5.insertId).toBe(1);
} catch(ex) {
// SHOULD NOT CATCH EXCEPTION on plugin:
if (!isWebSql) expect('EXCEPTION NOT EXPECTED on plugin with message: ' + ex.message).toBe('--');
// XXX TODO CHECK message, etc.
}
// knockoffs shall be ignored:
tx.executeSql('INSERT or IGNORE INTO characters VALUES (?,?,?)', ['Sonic', 'knockoffs4you', 0], function (txIgnored, rs6) {
// EXPECTED RESULT:
expect(rs6.rowsAffected).toBe(0);
// insertId plugin vs (WebKit) Web SQL:
if (isWebSql)
expect(rs6.insertId).toBe(1);
else
expect(rs6.insertId).not.toBeDefined();
done();
}, function(txIgnored, error) {
// ERROR NOT EXPECTED here - knockoff should have been ignored:
logError('knockoff should have been ignored');
expect(error.message).toBe('--');
done.fail();
});
});
});
});
});
});
}, function(error) {
// NOT EXPECTED:
expect(false).toBe(true);
expect(error.message).toBe('--');
// Close (plugin only) & finish:
(isWebSql) ? done() : db.close(done, done);
});
});
// FUTURE TODO: fix these tests to follow the Jasmine style and move into a separate spec file:
it(suiteName + "nested transaction test", function(done) {
var db = openDatabase("Database2", "1.0", "Demo", DEFAULT_SIZE);
expect(db).toBeTruthy(); // db object
db.transaction(function(tx) {
expect(tx).toBeTruthy(); // tx object
tx.executeSql('DROP TABLE IF EXISTS test_table');
tx.executeSql('CREATE TABLE IF NOT EXISTS test_table (id integer primary key, data text, data_num integer)');
tx.executeSql("INSERT INTO test_table (data, data_num) VALUES (?,?)", ["test", 100], function(tx, res) {
console.log("insertId: " + res.insertId + " -- probably 1");
console.log("rowsAffected: " + res.rowsAffected + " -- should be 1");
expect(res).toBeDefined();
expect(res.insertId).toBeDefined();
expect(res.rowsAffected).toBe(1);
tx.executeSql("select count(id) as cnt from test_table;", [], function(tx, res) {
console.log("res.rows.length: " + res.rows.length + " -- should be 1");
console.log("res.rows.item(0).cnt: " + res.rows.item(0).cnt + " -- should be 1");
expect(res.rows.length).toBe(1); // res rows length
expect(res.rows.item(0).cnt).toBe(1); // select count
done();
});
});
});
});
describe(suiteName + 'transaction callback semantics test(s)', function() {
function withTestTable(func) {
var db = openDatabase("Database", "1.0", "Demo", DEFAULT_SIZE);
db.transaction(function(tx) {
tx.executeSql('DROP TABLE IF EXISTS test_table');
tx.executeSql('CREATE TABLE IF NOT EXISTS test_table (id integer primary key, data text, data_num integer)');
}, function(err) { ok(false, err.message) }, function() {
//start();
func(db);
});
};
it(suiteName + "transaction encompasses all callbacks", function(done) {
var db = openDatabase("tx-all-callbacks.db", "1.0", "Demo", DEFAULT_SIZE);
db.transaction(function(tx) {
tx.executeSql('DROP TABLE IF EXISTS test_table');
tx.executeSql('CREATE TABLE IF NOT EXISTS test_table (id integer primary key, data text, data_num integer)');
db.transaction(function(tx) {
tx.executeSql('INSERT INTO test_table (data, data_num) VALUES (?,?)', ['test', 100], function(tx, res) {
tx.executeSql("SELECT count(*) as cnt from test_table", [], function(tx, res) {
expect(res.rows.item(0).cnt).toBe(1); // did insert row
throw new Error("deliberately aborting transaction");
});
});
}, function(error) {
if (!isWebSql) expect(error.message).toBe("deliberately aborting transaction");
db.transaction(function(tx) {
tx.executeSql("select count(*) as cnt from test_table", [], function(tx, res) {
// EXPECTED RESULT:
expect(res.rows.item(0).cnt).toBe(0); // final count shows we rolled back
done();
});
});
}, function() {
// ERROR NOT EXPECTED here:
logError("transaction succeeded but wasn't supposed to");done.fail();
expect(error.message).toBe('--');
start();
});
});
});
it(suiteName + 'exception from transaction handler causes failure', function(done) {
var db = openDatabase("exception-causes-failure.db", "1.0", "Demo", DEFAULT_SIZE);
try {
db.transaction(function(tx) {
throw new Error("boom");
}, function(error) {
expect(error).toBeDefined();
expect(error.code).toBeDefined();
expect(error.message).toBeDefined();
// error.hasOwnProperty('message') apparently NOT WORKING on
// WebKit Web SQL on Android 5.x/... or iOS 10.x/...:
if (!isWebSql || isWindows || (isAndroid && (/Android 4/.test(navigator.userAgent))))
expect(error.hasOwnProperty('message')).toBe(true);
expect(error.code).toBe(0);
if (isWebSql)
expect(error.message).toMatch(/the SQLTransactionCallback was null or threw an exception/);
else
expect(error.message).toBe('boom');
done();
}, function() {
// transaction success callback not expected
expect(false).toBe(true);
done();
});
logSuccess("db.transaction() did not throw an error");
} catch(ex) {
// exception not expected here
expect(false).toBe(true);
done();
}
});
it(suiteName + 'exception with code from transaction handler', function(done) {
var db = openDatabase("exception-with-code.db", "1.0", "Demo", DEFAULT_SIZE);
try {
db.transaction(function(tx) {
var e = new Error("boom");
e.code = 3;
throw e;
}, function(error) {
expect(error).toBeDefined();
expect(error.code).toBeDefined();
expect(error.message).toBeDefined();
if (isWebSql)
expect(error.code).toBe(0);
else
expect(error.code).toBe(3);
if (isWebSql)
expect(error.message).toMatch(/the SQLTransactionCallback was null or threw an exception/);
else
expect(error.message).toBe('boom');
done();
}, function() {
// transaction success callback not expected
expect(false).toBe(true);
done();
});
} catch(ex) {
// exception not expected here
expect(false).toBe(true);
done();
}
});
it(suiteName + "error handler returning true causes rollback", function(done) {
withTestTable(function(db) {
db.transaction(function(tx) {
tx.executeSql("insert into test_table (data, data_num) VALUES (?,?)", ['test', null], function(tx, res) {
expect(res).toBeDefined();
expect(res.rowsAffected).toBe(1);
tx.executeSql("select * from bogustable", [], function(tx, res) {
// NOT EXPECTED:
done.fail();
}, function(tx, err) {
// EXPECTED RESULT:
expect(err.message).toBeDefined();
return true;
});
});
}, function(err) {
// EXPECTED RESULT:
expect(err.message).toBeTruthy(); // should report error message
db.transaction(function(tx) {
tx.executeSql("select count(*) as cnt from test_table", [], function(tx, res) {
// EXPECTED RESULT:
expect(res.rows.item(0).cnt).toBe(0); // should have rolled back
done();
});
});
}, function() {
// NOT EXPECTED - not supposed to succeed:
logError('not supposed to succeed');
expect(error.message).toBe('--');
done.fail();
});
});
});
// NOTE: conclusion reached with @aarononeal and @nolanlawson in litehelpers/Cordova-sqlite-storage#232
// that the according to the spec at http://www.w3.org/TR/webdatabase/ the transaction should be
// recovered *only* if the sql error handler returns false.
it(suiteName + 'error handler returning false lets transaction continue', function(done) {
var check1 = false;
withTestTable(function(db) {
db.transaction(function(tx) {
tx.executeSql("insert into test_table (data, data_num) VALUES (?,?)", ['test', null], function(tx, res) {
expect(res).toBeDefined();
expect(res.rowsAffected).toBe(1);
tx.executeSql("select * from bogustable", [], function(tx, res) {
// NOT EXPECTED:
logError("select statement not supposed to succeed");
done.fail();
}, function(tx, err) {
// EXPECTED RESULT:
check1 = true;
expect(err.message).toBeTruthy(); // should report a valid error message
return false;
});
});
}, function(error) {
// ERROR NOT EXPECTED here:
logError('transaction was supposed to succeed: ' + error.message);
expect(error.message).toBe('--');
done.fail();
}, function() {
db.transaction(function(tx) {
tx.executeSql("select count(*) as cnt from test_table", [], function(tx, res) {
expect(check1).toBe(true);
expect(res.rows.item(0).cnt).toBe(1); // should have commited
done();
});
});
});
});
});
it(suiteName + "missing error handler causes rollback", function(done) {
withTestTable(function(db) {
db.transaction(function(tx) {
tx.executeSql("insert into test_table (data, data_num) VALUES (?,?)", ['test', null], function(tx, res) {
expect(res).toBeDefined();
expect(res.rowsAffected).toEqual(1);
tx.executeSql("select * from bogustable", [], function(tx, res) {
// NOT EXPECTED:
logError("select statement not supposed to succeed");
done.fail();
});
});
}, function(err) {
// EXPECTED RESULT:
expect(err.message).toBeTruthy(); // should report a valid error message
db.transaction(function(tx) {
tx.executeSql("select count(*) as cnt from test_table", [], function(tx, res) {
expect(res.rows.item(0).cnt).toBe(0); // should have rolled back
done();
});
});
}, function() {
// ERROR NOT EXPECTED here:
logError("transaction was supposed to fail");done.fail();
expect(error.message).toBe('--');
done.fail();
});
});
});
it(suiteName + "executeSql fails outside transaction", function(done) {
var check1 = false;
withTestTable(function(db) {
expect(db).toBeTruthy(); // db ok
var txg;
db.transaction(function(tx) {
expect(tx).toBeTruthy(); // tx ok
txg = tx;
tx.executeSql("insert into test_table (data, data_num) VALUES (?,?)", ['test', null], function(tx, res) {
expect(res).toBeDefined();
expect(res.rowsAffected).toEqual(1);
check1 = true;
});
}, function(error) {
// ERROR NOT EXPECTED here:
logError('unexpected error callback with message: ' + error.message);
expect(error.message).toBe('--');
done.fail();
}, function() {
// this simulates what would happen if a Promise ran on the next tick
// and invoked an execute on the transaction
try {
txg.executeSql("select count(*) as cnt from test_table", [], null, null);
// NOT EXPECTED to get here:
logError("executeSql should have thrown but continued instead");
done.fail();
} catch(err) {
expect(err.message).toBeTruthy(); // error had valid message
}
done();
});
});
});
});
it(suiteName + "readTransaction should fail & report error on modification", function(done) {
var db = openDatabase("tx-readonly-test.db", "1.0", "Demo", DEFAULT_SIZE);
db.transaction(function(tx) {
tx.executeSql('DROP TABLE IF EXISTS test_table');
tx.executeSql('DROP TABLE IF EXISTS ExtraTestTable1');
tx.executeSql('DROP TABLE IF EXISTS ExtraTestTable2');
tx.executeSql('DROP TABLE IF EXISTS ExtraTestTable3');
tx.executeSql('DROP TABLE IF EXISTS ExtraTestTable4');
tx.executeSql('DROP TABLE IF EXISTS ExtraTestTable5');
tx.executeSql('DROP TABLE IF EXISTS ExtraTestTable6');
tx.executeSql('DROP TABLE IF EXISTS AlterTestTable');
tx.executeSql('CREATE TABLE test_table (data)');
tx.executeSql('INSERT INTO test_table VALUES (?)', ['first']);
tx.executeSql('CREATE TABLE AlterTestTable (FirstColumn)');
}, function () {}, function () {
db.readTransaction(function (tx) {
tx.executeSql('SELECT * from test_table', [], function (tx, res) {
expect(res.rows.length).toBe(1);
expect(res.rows.item(0).data).toBe('first');
});
}, function () {}, function () {
var numDone = 0;
var failed = false;
var tasks;
function checkDone() {
if (++numDone === tasks.length) {
done();
}
}
function fail() {
if (!failed) {
expect(false).toBe(true);
expect('readTransaction was supposed to fail').toBe('--');
failed = true;
done();
}
}
tasks = [
// these transactions should be OK:
function () {
db.readTransaction(function (tx) {
tx.executeSql(' SELECT 1;');
}, fail, checkDone);
},
function () {
db.readTransaction(function (tx) {
tx.executeSql('; SELECT 1;');
}, fail, checkDone);
},
// all of these transactions should report an error
function () {
db.readTransaction(function (tx) {
tx.executeSql('UPDATE test_table SET foo = "another"');
}, checkDone, fail);
},
function () {
db.readTransaction(function (tx) {
tx.executeSql('INSERT INTO test_table VALUES ("another")');
}, checkDone, fail);
},
function () {
db.readTransaction(function (tx) {
tx.executeSql('DELETE from test_table');
}, checkDone, fail);
},
function () {
db.readTransaction(function (tx) {
tx.executeSql('DROP TABLE test_table');
}, checkDone, fail);
},
function () {
db.readTransaction(function (tx) {
// extra space before sql (OK)
tx.executeSql(' CREATE TABLE test_table2 (data)');
}, checkDone, fail);
},
function () {
db.readTransaction(function (tx) {
// two extra spaces before sql (OK)
tx.executeSql(' CREATE TABLE test_table3 (data)');
}, checkDone, fail);
},
function () {
db.readTransaction(function (tx) {
tx.executeSql('; CREATE TABLE ExtraTestTable1 (data)');
}, checkDone, fail);
},
function () {
db.readTransaction(function (tx) {
tx.executeSql(' ; CREATE TABLE ExtraTestTable2 (data)');
}, checkDone, fail);
},
function () {
db.readTransaction(function (tx) {
tx.executeSql(';CREATE TABLE ExtraTestTable3 (data)');
}, checkDone, fail);
},
function () {
db.readTransaction(function (tx) {
tx.executeSql(';; CREATE TABLE ExtraTestTable4 (data)');
}, checkDone, fail);
},
function () {
db.readTransaction(function (tx) {
tx.executeSql('; ;CREATE TABLE ExtraTestTable5 (data)');
}, checkDone, fail);
},
function () {
db.readTransaction(function (tx) {
tx.executeSql('; ; CREATE TABLE ExtraTestTable6 (data)');
}, checkDone, fail);
},
function () {
db.readTransaction(function (tx) {
tx.executeSql('ALTER TABLE AlterTestTable ADD COLUMN NewColumn');
}, checkDone, fail);
},
function () {
db.readTransaction(function (tx) {
tx.executeSql('REINDEX');
}, checkDone, fail);
},
function () {
db.readTransaction(function (tx) {
tx.executeSql('REPLACE INTO test_table VALUES ("another")');
}, checkDone, fail);
},
];
for (var i = 0; i < tasks.length; i++) {
tasks[i]();
}
});
});
});
it(suiteName + ' test callback order', function (done) {
var db = openDatabase("Database-Callback-Order", "1.0", "Demo", DEFAULT_SIZE);
var blocked = true;
db.transaction(function(tx) {
// callback to the transaction shouldn't block (1)
expect(blocked).toBe(false);
tx.executeSql('SELECT 1', [], function () {
// callback to the transaction shouldn't block (2)
expect(blocked).toBe(false);
});
}, function(err) { ok(false, err.message) }, function() {
// callback to the transaction shouldn't block (3)
expect(blocked).toBe(false);
done();
});
blocked = false;
});
});
}
}
if (window.hasBrowser) mytests();
else exports.defineAutoTests = mytests;
/* vim: set expandtab : */