Add v3 compatibility end-to-end test

This is still a WIP. It adds the basic skeleton for the tests and is going through the signup, but currently unable to finish.
This commit is contained in:
Bruno Bernardino 2022-02-09 17:15:08 +00:00
parent aae84f4e22
commit 5aa2d19865
No known key found for this signature in database
GPG Key ID: D1B0A69ADD114ECE
61 changed files with 68607 additions and 107 deletions

View File

@ -11,3 +11,4 @@ packages/tauri/src-tauri/target/**/*
packages/tauri/tauri-update.json
packages/pwa/dist/**/*
package-lock.json
cypress/fixtures/**/*

View File

@ -1,5 +1,7 @@
{
"email": "user@example.com",
"password": "password",
"emailToken": "000000"
"name": "The Dude",
"emailToken": "000000",
"v3_url": "http://localhost:8081"
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 54 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 579 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@ -0,0 +1,71 @@
<!DOCTYPE html>
<html>
<head>
<title>Padloc</title>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"
/>
<meta name="apple-mobile-web-app-capable" content="yes" />
<link rel="apple-touch-icon" href="/assets/favicon.png">
<style>
html,
body {
background: linear-gradient(rgb(89, 198, 255), rgb(7, 124, 185));
width: 100%;
height: 100%;
margin: 0;
overscroll-behavior: none;
color: #fff;
position: fixed;
}
@keyframes spin {
from {
stroke-dashoffset: 240;
}
to {
stroke-dashoffset: 40;
}
}
@keyframes rotate {
to {
transform: rotate(360deg);
}
}
.spinner {
width: 50px;
height: 50px;
position: absolute;
top: 0;
right: 0;
left: 0;
bottom: 0;
margin: auto;
}
.spinner circle {
fill: none;
stroke: currentColor;
stroke-linecap: round;
stroke-width: 10;
stroke-dasharray: 252;
transform-origin: center center;
will-change: transform;
animation: spin 1.5s cubic-bezier(0.44, 0.22, 0.64, 0.86) alternate infinite,
rotate linear 1.2s infinite;
}
</style>
<meta http-equiv="Content-Security-Policy" content="default-src 'self' http://0.0.0.0:3000 blob:; style-src 'self' 'unsafe-inline'; object-src 'self' blob:; frame-src 'self' blob: ; img-src 'self' blob:"><script defer src="/main.js"></script><link rel="manifest" href="/manifest.41bf06683f2015ae2f4f1a35e4d2873a.json" /></head>
<body>
<svg viewBox="0 0 100 100" class="spinner">
<circle cx="50" cy="50" r="40" />
</svg>
</body>
</html>

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,41 @@
{
"icons": [
{
"src": "/icon_512x512.a010485cd9c6e57ff248fd2b3326584d.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "/icon_384x384.6e43a89ef1b6a6a621bb761eba8c55c7.png",
"sizes": "384x384",
"type": "image/png"
},
{
"src": "/icon_256x256.472eb7710503d2d60f634bdbd0a6f6bc.png",
"sizes": "256x256",
"type": "image/png"
},
{
"src": "/icon_192x192.02182e390dd5bfda29a85b4ed8ee99e4.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icon_128x128.1934781ac166ce1182473922419945cf.png",
"sizes": "128x128",
"type": "image/png"
},
{
"src": "/icon_96x96.bf1f4e1a1e7b38d6e4daf7b786bdd376.png",
"sizes": "96x96",
"type": "image/png"
}
],
"name": "Padloc Password Manager",
"short_name": "Padloc",
"orientation": "portrait",
"display": "standalone",
"start_url": ".",
"background_color": "#59c6ff",
"theme": "#59c6ff"
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,913 @@
(self["webpackChunk_padloc_pwa"] = self["webpackChunk_padloc_pwa"] || []).push([["ua-parser"],{
/***/ "../app/node_modules/ua-parser-js/src/ua-parser.js":
/*!*********************************************************!*\
!*** ../app/node_modules/ua-parser-js/src/ua-parser.js ***!
\*********************************************************/
/***/ (function(module, exports, __webpack_require__) {
var __WEBPACK_AMD_DEFINE_RESULT__;/*!@license
* UAParser.js v0.7.28
* Lightweight JavaScript-based User-Agent string parser
* https://github.com/faisalman/ua-parser-js
*
* Copyright © 2012-2021 Faisal Salman <f@faisalman.com>
* Licensed under MIT License
*/
(function (window, undefined) {
'use strict';
//////////////
// Constants
/////////////
var LIBVERSION = '0.7.28',
EMPTY = '',
UNKNOWN = '?',
FUNC_TYPE = 'function',
UNDEF_TYPE = 'undefined',
OBJ_TYPE = 'object',
STR_TYPE = 'string',
MAJOR = 'major', // deprecated
MODEL = 'model',
NAME = 'name',
TYPE = 'type',
VENDOR = 'vendor',
VERSION = 'version',
ARCHITECTURE= 'architecture',
CONSOLE = 'console',
MOBILE = 'mobile',
TABLET = 'tablet',
SMARTTV = 'smarttv',
WEARABLE = 'wearable',
EMBEDDED = 'embedded',
UA_MAX_LENGTH = 255;
///////////
// Helper
//////////
var util = {
extend : function (regexes, extensions) {
var mergedRegexes = {};
for (var i in regexes) {
if (extensions[i] && extensions[i].length % 2 === 0) {
mergedRegexes[i] = extensions[i].concat(regexes[i]);
} else {
mergedRegexes[i] = regexes[i];
}
}
return mergedRegexes;
},
has : function (str1, str2) {
return typeof str1 === STR_TYPE ? str2.toLowerCase().indexOf(str1.toLowerCase()) !== -1 : false;
},
lowerize : function (str) {
return str.toLowerCase();
},
major : function (version) {
return typeof(version) === STR_TYPE ? version.replace(/[^\d\.]/g,'').split(".")[0] : undefined;
},
trim : function (str, len) {
str = str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
return typeof(len) === UNDEF_TYPE ? str : str.substring(0, UA_MAX_LENGTH);
}
};
///////////////
// Map helper
//////////////
var mapper = {
rgx : function (ua, arrays) {
var i = 0, j, k, p, q, matches, match;
// loop through all regexes maps
while (i < arrays.length && !matches) {
var regex = arrays[i], // even sequence (0,2,4,..)
props = arrays[i + 1]; // odd sequence (1,3,5,..)
j = k = 0;
// try matching uastring with regexes
while (j < regex.length && !matches) {
matches = regex[j++].exec(ua);
if (!!matches) {
for (p = 0; p < props.length; p++) {
match = matches[++k];
q = props[p];
// check if given property is actually array
if (typeof q === OBJ_TYPE && q.length > 0) {
if (q.length == 2) {
if (typeof q[1] == FUNC_TYPE) {
// assign modified match
this[q[0]] = q[1].call(this, match);
} else {
// assign given value, ignore regex match
this[q[0]] = q[1];
}
} else if (q.length == 3) {
// check whether function or regex
if (typeof q[1] === FUNC_TYPE && !(q[1].exec && q[1].test)) {
// call function (usually string mapper)
this[q[0]] = match ? q[1].call(this, match, q[2]) : undefined;
} else {
// sanitize match using given regex
this[q[0]] = match ? match.replace(q[1], q[2]) : undefined;
}
} else if (q.length == 4) {
this[q[0]] = match ? q[3].call(this, match.replace(q[1], q[2])) : undefined;
}
} else {
this[q] = match ? match : undefined;
}
}
}
}
i += 2;
}
},
str : function (str, map) {
for (var i in map) {
// check if array
if (typeof map[i] === OBJ_TYPE && map[i].length > 0) {
for (var j = 0; j < map[i].length; j++) {
if (util.has(map[i][j], str)) {
return (i === UNKNOWN) ? undefined : i;
}
}
} else if (util.has(map[i], str)) {
return (i === UNKNOWN) ? undefined : i;
}
}
return str;
}
};
///////////////
// String map
//////////////
var maps = {
browser : {
// Safari < 3.0
oldSafari : {
version : {
'1.0' : '/8',
'1.2' : '/1',
'1.3' : '/3',
'2.0' : '/412',
'2.0.2' : '/416',
'2.0.3' : '/417',
'2.0.4' : '/419',
'?' : '/'
}
},
oldEdge : {
version : {
'0.1' : '12.',
'21' : '13.',
'31' : '14.',
'39' : '15.',
'41' : '16.',
'42' : '17.',
'44' : '18.'
}
}
},
os : {
windows : {
version : {
'ME' : '4.90',
'NT 3.11' : 'NT3.51',
'NT 4.0' : 'NT4.0',
'2000' : 'NT 5.0',
'XP' : ['NT 5.1', 'NT 5.2'],
'Vista' : 'NT 6.0',
'7' : 'NT 6.1',
'8' : 'NT 6.2',
'8.1' : 'NT 6.3',
'10' : ['NT 6.4', 'NT 10.0'],
'RT' : 'ARM'
}
}
}
};
//////////////
// Regex map
/////////////
var regexes = {
browser : [[
/\b(?:crmo|crios)\/([\w\.]+)/i // Chrome for Android/iOS
], [VERSION, [NAME, 'Chrome']], [
/edg(?:e|ios|a)?\/([\w\.]+)/i // Microsoft Edge
], [VERSION, [NAME, 'Edge']], [
// breaking change (reserved for next major release):
///edge\/([\w\.]+)/i // Old Edge (Trident)
//], [[VERSION, mapper.str, maps.browser.oldEdge.version], [NAME, 'Edge']], [
// Presto based
/(opera\smini)\/([\w\.-]+)/i, // Opera Mini
/(opera\s[mobiletab]{3,6})\b.+version\/([\w\.-]+)/i, // Opera Mobi/Tablet
/(opera)(?:.+version\/|[\/\s]+)([\w\.]+)/i, // Opera
], [NAME, VERSION], [
/opios[\/\s]+([\w\.]+)/i // Opera mini on iphone >= 8.0
], [VERSION, [NAME, 'Opera Mini']], [
/\sopr\/([\w\.]+)/i // Opera Webkit
], [VERSION, [NAME, 'Opera']], [
// Mixed
/(kindle)\/([\w\.]+)/i, // Kindle
/(lunascape|maxthon|netfront|jasmine|blazer)[\/\s]?([\w\.]*)/i, // Lunascape/Maxthon/Netfront/Jasmine/Blazer
// Trident based
/(avant\s|iemobile|slim)(?:browser)?[\/\s]?([\w\.]*)/i, // Avant/IEMobile/SlimBrowser
/(ba?idubrowser)[\/\s]?([\w\.]+)/i, // Baidu Browser
/(?:ms|\()(ie)\s([\w\.]+)/i, // Internet Explorer
// Webkit/KHTML based
/(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark|qupzilla|falkon)\/([\w\.-]+)/i,
// Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon
/(rekonq|puffin|brave|whale|qqbrowserlite|qq)\/([\w\.]+)/i, // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ, aka ShouQ
/(weibo)__([\d\.]+)/i // Weibo
], [NAME, VERSION], [
/(?:[\s\/]uc?\s?browser|(?:juc.+)ucweb)[\/\s]?([\w\.]+)/i // UCBrowser
], [VERSION, [NAME, 'UCBrowser']], [
/(?:windowswechat)?\sqbcore\/([\w\.]+)\b.*(?:windowswechat)?/i // WeChat Desktop for Windows Built-in Browser
], [VERSION, [NAME, 'WeChat(Win) Desktop']], [
/micromessenger\/([\w\.]+)/i // WeChat
], [VERSION, [NAME, 'WeChat']], [
/konqueror\/([\w\.]+)/i // Konqueror
], [VERSION, [NAME, 'Konqueror']], [
/trident.+rv[:\s]([\w\.]{1,9})\b.+like\sgecko/i // IE11
], [VERSION, [NAME, 'IE']], [
/yabrowser\/([\w\.]+)/i // Yandex
], [VERSION, [NAME, 'Yandex']], [
/(avast|avg)\/([\w\.]+)/i // Avast/AVG Secure Browser
], [[NAME, /(.+)/, '$1 Secure Browser'], VERSION], [
/focus\/([\w\.]+)/i // Firefox Focus
], [VERSION, [NAME, 'Firefox Focus']], [
/opt\/([\w\.]+)/i // Opera Touch
], [VERSION, [NAME, 'Opera Touch']], [
/coc_coc_browser\/([\w\.]+)/i // Coc Coc Browser
], [VERSION, [NAME, 'Coc Coc']], [
/dolfin\/([\w\.]+)/i // Dolphin
], [VERSION, [NAME, 'Dolphin']], [
/coast\/([\w\.]+)/i // Opera Coast
], [VERSION, [NAME, 'Opera Coast']],
[/xiaomi\/miuibrowser\/([\w\.]+)/i // MIUI Browser
], [VERSION, [NAME, 'MIUI Browser']], [
/fxios\/([\w\.-]+)/i // Firefox for iOS
], [VERSION, [NAME, 'Firefox']], [
/(qihu|qhbrowser|qihoobrowser|360browser)/i // 360
], [[NAME, '360 Browser']], [
/(oculus|samsung|sailfish)browser\/([\w\.]+)/i
], [[NAME, /(.+)/, '$1 Browser'], VERSION], [ // Oculus/Samsung/Sailfish Browser
/(comodo_dragon)\/([\w\.]+)/i // Comodo Dragon
], [[NAME, /_/g, ' '], VERSION], [
/\s(electron)\/([\w\.]+)\ssafari/i, // Electron-based App
/(tesla)(?:\sqtcarbrowser|\/(20[12]\d\.[\w\.-]+))/i, // Tesla
/m?(qqbrowser|baiduboxapp|2345Explorer)[\/\s]?([\w\.]+)/i // QQBrowser/Baidu App/2345 Browser
], [NAME, VERSION], [
/(MetaSr)[\/\s]?([\w\.]+)/i, // SouGouBrowser
/(LBBROWSER)/i // LieBao Browser
], [NAME], [
// WebView
/;fbav\/([\w\.]+);/i // Facebook App for iOS & Android with version
], [VERSION, [NAME, 'Facebook']], [
/FBAN\/FBIOS|FB_IAB\/FB4A/i // Facebook App for iOS & Android without version
], [[NAME, 'Facebook']], [
/safari\s(line)\/([\w\.]+)/i, // Line App for iOS
/\b(line)\/([\w\.]+)\/iab/i, // Line App for Android
/(chromium|instagram)[\/\s]([\w\.-]+)/i // Chromium/Instagram
], [NAME, VERSION], [
/\bgsa\/([\w\.]+)\s.*safari\//i // Google Search Appliance on iOS
], [VERSION, [NAME, 'GSA']], [
/headlesschrome(?:\/([\w\.]+)|\s)/i // Chrome Headless
], [VERSION, [NAME, 'Chrome Headless']], [
/\swv\).+(chrome)\/([\w\.]+)/i // Chrome WebView
], [[NAME, 'Chrome WebView'], VERSION], [
/droid.+\sversion\/([\w\.]+)\b.+(?:mobile\ssafari|safari)/i // Android Browser
], [VERSION, [NAME, 'Android Browser']], [
/(chrome|omniweb|arora|[tizenoka]{5}\s?browser)\/v?([\w\.]+)/i // Chrome/OmniWeb/Arora/Tizen/Nokia
], [NAME, VERSION], [
/version\/([\w\.]+)\s.*mobile\/\w+\s(safari)/i // Mobile Safari
], [VERSION, [NAME, 'Mobile Safari']], [
/version\/([\w\.]+)\s.*(mobile\s?safari|safari)/i // Safari & Safari Mobile
], [VERSION, NAME], [
/webkit.+?(mobile\s?safari|safari)(\/[\w\.]+)/i // Safari < 3.0
], [NAME, [VERSION, mapper.str, maps.browser.oldSafari.version]], [
/(webkit|khtml)\/([\w\.]+)/i
], [NAME, VERSION], [
// Gecko based
/(navigator|netscape)\/([\w\.-]+)/i // Netscape
], [[NAME, 'Netscape'], VERSION], [
/ile\svr;\srv:([\w\.]+)\).+firefox/i // Firefox Reality
], [VERSION, [NAME, 'Firefox Reality']], [
/ekiohf.+(flow)\/([\w\.]+)/i, // Flow
/(swiftfox)/i, // Swiftfox
/(icedragon|iceweasel|camino|chimera|fennec|maemo\sbrowser|minimo|conkeror)[\/\s]?([\w\.\+]+)/i,
// IceDragon/Iceweasel/Camino/Chimera/Fennec/Maemo/Minimo/Conkeror
/(firefox|seamonkey|k-meleon|icecat|iceape|firebird|phoenix|palemoon|basilisk|waterfox)\/([\w\.-]+)$/i,
// Firefox/SeaMonkey/K-Meleon/IceCat/IceApe/Firebird/Phoenix
/(firefox)\/([\w\.]+)\s[\w\s\-]+\/[\w\.]+$/i, // Other Firefox-based
/(mozilla)\/([\w\.]+)\s.+rv\:.+gecko\/\d+/i, // Mozilla
// Other
/(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf|sleipnir)[\/\s]?([\w\.]+)/i,
// Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/Sleipnir
/(links)\s\(([\w\.]+)/i, // Links
/(gobrowser)\/?([\w\.]*)/i, // GoBrowser
/(ice\s?browser)\/v?([\w\._]+)/i, // ICE Browser
/(mosaic)[\/\s]([\w\.]+)/i // Mosaic
], [NAME, VERSION]
],
cpu : [[
/(?:(amd|x(?:(?:86|64)[_-])?|wow|win)64)[;\)]/i // AMD64 (x64)
], [[ARCHITECTURE, 'amd64']], [
/(ia32(?=;))/i // IA32 (quicktime)
], [[ARCHITECTURE, util.lowerize]], [
/((?:i[346]|x)86)[;\)]/i // IA32 (x86)
], [[ARCHITECTURE, 'ia32']], [
/\b(aarch64|armv?8e?l?)\b/i // ARM64
], [[ARCHITECTURE, 'arm64']], [
/\b(arm(?:v[67])?ht?n?[fl]p?)\b/i // ARMHF
], [[ARCHITECTURE, 'armhf']], [
// PocketPC mistakenly identified as PowerPC
/windows\s(ce|mobile);\sppc;/i
], [[ARCHITECTURE, 'arm']], [
/((?:ppc|powerpc)(?:64)?)(?:\smac|;|\))/i // PowerPC
], [[ARCHITECTURE, /ower/, '', util.lowerize]], [
/(sun4\w)[;\)]/i // SPARC
], [[ARCHITECTURE, 'sparc']], [
/((?:avr32|ia64(?=;))|68k(?=\))|\barm(?:64|(?=v(?:[1-7]|[5-7]1)l?|;|eabi))|(?=atmel\s)avr|(?:irix|mips|sparc)(?:64)?\b|pa-risc)/i
// IA64, 68K, ARM/64, AVR/32, IRIX/64, MIPS/64, SPARC/64, PA-RISC
], [[ARCHITECTURE, util.lowerize]]
],
device : [[
//////////////////////////
// MOBILES & TABLETS
// Ordered by popularity
/////////////////////////
// Samsung
/\b(sch-i[89]0\d|shw-m380s|sm-[pt]\w{2,4}|gt-[pn]\d{2,4}|sgh-t8[56]9|nexus\s10)/i
], [MODEL, [VENDOR, 'Samsung'], [TYPE, TABLET]], [
/\b((?:s[cgp]h|gt|sm)-\w+|galaxy\snexus)/i,
/\ssamsung[\s-]([\w-]+)/i,
/sec-(sgh\w+)/i
], [MODEL, [VENDOR, 'Samsung'], [TYPE, MOBILE]], [
// Apple
/\((ip(?:hone|od)[\s\w]*);/i // iPod/iPhone
], [MODEL, [VENDOR, 'Apple'], [TYPE, MOBILE]], [
/\((ipad);[\w\s\),;-]+apple/i, // iPad
/applecoremedia\/[\w\.]+\s\((ipad)/i,
/\b(ipad)\d\d?,\d\d?[;\]].+ios/i
], [MODEL, [VENDOR, 'Apple'], [TYPE, TABLET]], [
// Huawei
/\b((?:agr|ags[23]|bah2?|sht?)-a?[lw]\d{2})/i,
], [MODEL, [VENDOR, 'Huawei'], [TYPE, TABLET]], [
/d\/huawei([\w\s-]+)[;\)]/i,
/\b(nexus\s6p|vog-[at]?l\d\d|ane-[at]?l[x\d]\d|eml-a?l\d\da?|lya-[at]?l\d[\dc]|clt-a?l\d\di?|ele-l\d\d)/i,
/\b(\w{2,4}-[atu][ln][01259][019])[;\)\s]/i
], [MODEL, [VENDOR, 'Huawei'], [TYPE, MOBILE]], [
// Xiaomi
/\b(poco[\s\w]+)(?:\sbuild|\))/i, // Xiaomi POCO
/\b;\s(\w+)\sbuild\/hm\1/i, // Xiaomi Hongmi 'numeric' models
/\b(hm[\s\-_]?note?[\s_]?(?:\d\w)?)\sbuild/i, // Xiaomi Hongmi
/\b(redmi[\s\-_]?(?:note|k)?[\w\s_]+)(?:\sbuild|\))/i, // Xiaomi Redmi
/\b(mi[\s\-_]?(?:a\d|one|one[\s_]plus|note lte)?[\s_]?(?:\d?\w?)[\s_]?(?:plus)?)\sbuild/i // Xiaomi Mi
], [[MODEL, /_/g, ' '], [VENDOR, 'Xiaomi'], [TYPE, MOBILE]], [
/\b(mi[\s\-_]?(?:pad)(?:[\w\s_]+))(?:\sbuild|\))/i // Mi Pad tablets
],[[MODEL, /_/g, ' '], [VENDOR, 'Xiaomi'], [TYPE, TABLET]], [
// OPPO
/;\s(\w+)\sbuild.+\soppo/i,
/\s(cph[12]\d{3}|p(?:af|c[al]|d\w|e[ar])[mt]\d0|x9007)\b/i
], [MODEL, [VENDOR, 'OPPO'], [TYPE, MOBILE]], [
// Vivo
/\svivo\s(\w+)(?:\sbuild|\))/i,
/\s(v[12]\d{3}\w?[at])(?:\sbuild|;)/i
], [MODEL, [VENDOR, 'Vivo'], [TYPE, MOBILE]], [
// Realme
/\s(rmx[12]\d{3})(?:\sbuild|;)/i
], [MODEL, [VENDOR, 'Realme'], [TYPE, MOBILE]], [
// Motorola
/\s(milestone|droid(?:[2-4x]|\s(?:bionic|x2|pro|razr))?:?(\s4g)?)\b[\w\s]+build\//i,
/\smot(?:orola)?[\s-](\w*)/i,
/((?:moto[\s\w\(\)]+|xt\d{3,4}|nexus\s6)(?=\sbuild|\)))/i
], [MODEL, [VENDOR, 'Motorola'], [TYPE, MOBILE]], [
/\s(mz60\d|xoom[\s2]{0,2})\sbuild\//i
], [MODEL, [VENDOR, 'Motorola'], [TYPE, TABLET]], [
// LG
/((?=lg)?[vl]k\-?\d{3})\sbuild|\s3\.[\s\w;-]{10}lg?-([06cv9]{3,4})/i
], [MODEL, [VENDOR, 'LG'], [TYPE, TABLET]], [
/(lm-?f100[nv]?|nexus\s[45])/i,
/lg[e;\s\/-]+((?!browser|netcast)\w+)/i,
/\blg(\-?[\d\w]+)\sbuild/i
], [MODEL, [VENDOR, 'LG'], [TYPE, MOBILE]], [
// Lenovo
/(ideatab[\w\-\s]+)/i,
/lenovo\s?(s(?:5000|6000)(?:[\w-]+)|tab(?:[\s\w]+)|yt[\d\w-]{6}|tb[\d\w-]{6})/i // Lenovo tablets
], [MODEL, [VENDOR, 'Lenovo'], [TYPE, TABLET]], [
// Nokia
/(?:maemo|nokia).*(n900|lumia\s\d+)/i,
/nokia[\s_-]?([\w\.-]*)/i
], [[MODEL, /_/g, ' '], [VENDOR, 'Nokia'], [TYPE, MOBILE]], [
// Google
/droid.+;\s(pixel\sc)[\s)]/i // Google Pixel C
], [MODEL, [VENDOR, 'Google'], [TYPE, TABLET]], [
/droid.+;\s(pixel[\s\daxl]{0,6})(?:\sbuild|\))/i // Google Pixel
], [MODEL, [VENDOR, 'Google'], [TYPE, MOBILE]], [
// Sony
/droid.+\s([c-g]\d{4}|so[-l]\w+|xq-a\w[4-7][12])(?=\sbuild\/|\).+chrome\/(?![1-6]{0,1}\d\.))/i
], [MODEL, [VENDOR, 'Sony'], [TYPE, MOBILE]], [
/sony\stablet\s[ps]\sbuild\//i,
/(?:sony)?sgp\w+(?:\sbuild\/|\))/i
], [[MODEL, 'Xperia Tablet'], [VENDOR, 'Sony'], [TYPE, TABLET]], [
// OnePlus
/\s(kb2005|in20[12]5|be20[12][59])\b/i,
/\ba000(1)\sbuild/i, // OnePlus
/\boneplus\s(a\d{4})[\s)]/i
], [MODEL, [VENDOR, 'OnePlus'], [TYPE, MOBILE]], [
// Amazon
/(alexa)webm/i,
/(kf[a-z]{2}wi)(\sbuild\/|\))/i, // Kindle Fire without Silk
/(kf[a-z]+)(\sbuild\/|\)).+silk\//i // Kindle Fire HD
], [MODEL, [VENDOR, 'Amazon'], [TYPE, TABLET]], [
/(sd|kf)[0349hijorstuw]+(\sbuild\/|\)).+silk\//i // Fire Phone
], [[MODEL, 'Fire Phone'], [VENDOR, 'Amazon'], [TYPE, MOBILE]], [
// BlackBerry
/\((playbook);[\w\s\),;-]+(rim)/i // BlackBerry PlayBook
], [MODEL, VENDOR, [TYPE, TABLET]], [
/((?:bb[a-f]|st[hv])100-\d)/i,
/\(bb10;\s(\w+)/i // BlackBerry 10
], [MODEL, [VENDOR, 'BlackBerry'], [TYPE, MOBILE]], [
// Asus
/(?:\b|asus_)(transfo[prime\s]{4,10}\s\w+|eeepc|slider\s\w+|nexus\s7|padfone|p00[cj])/i
], [MODEL, [VENDOR, 'ASUS'], [TYPE, TABLET]], [
/\s(z[es]6[027][01][km][ls]|zenfone\s\d\w?)\b/i
], [MODEL, [VENDOR, 'ASUS'], [TYPE, MOBILE]], [
// HTC
/(nexus\s9)/i // HTC Nexus 9
], [MODEL, [VENDOR, 'HTC'], [TYPE, TABLET]], [
/(htc)[;_\s-]{1,2}([\w\s]+(?=\)|\sbuild)|\w+)/i, // HTC
// ZTE
/(zte)-(\w*)/i,
/(alcatel|geeksphone|nexian|panasonic|(?=;\s)sony)[_\s-]?([\w-]*)/i // Alcatel/GeeksPhone/Nexian/Panasonic/Sony
], [VENDOR, [MODEL, /_/g, ' '], [TYPE, MOBILE]], [
// Acer
/droid[x\d\.\s;]+\s([ab][1-7]\-?[0178a]\d\d?)/i
], [MODEL, [VENDOR, 'Acer'], [TYPE, TABLET]], [
// Meizu
/droid.+;\s(m[1-5]\snote)\sbuild/i,
/\bmz-([\w-]{2,})/i
], [MODEL, [VENDOR, 'Meizu'], [TYPE, MOBILE]], [
// MIXED
/(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron)[\s_-]?([\w-]*)/i,
// BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron
/(hp)\s([\w\s]+\w)/i, // HP iPAQ
/(asus)-?(\w+)/i, // Asus
/(microsoft);\s(lumia[\s\w]+)/i, // Microsoft Lumia
/(lenovo)[_\s-]?([\w-]+)/i, // Lenovo
/linux;.+(jolla);/i, // Jolla
/droid.+;\s(oppo)\s?([\w\s]+)\sbuild/i // OPPO
], [VENDOR, MODEL, [TYPE, MOBILE]], [
/(archos)\s(gamepad2?)/i, // Archos
/(hp).+(touchpad(?!.+tablet)|tablet)/i, // HP TouchPad
/(kindle)\/([\w\.]+)/i, // Kindle
/\s(nook)[\w\s]+build\/(\w+)/i, // Nook
/(dell)\s(strea[kpr\s\d]*[\dko])/i, // Dell Streak
/[;\/]\s?(le[\s\-]+pan)[\s\-]+(\w{1,9})\sbuild/i, // Le Pan Tablets
/[;\/]\s?(trinity)[\-\s]*(t\d{3})\sbuild/i, // Trinity Tablets
/\b(gigaset)[\s\-]+(q\w{1,9})\sbuild/i, // Gigaset Tablets
/\b(vodafone)\s([\w\s]+)(?:\)|\sbuild)/i // Vodafone
], [VENDOR, MODEL, [TYPE, TABLET]], [
/\s(surface\sduo)\s/i // Surface Duo
], [MODEL, [VENDOR, 'Microsoft'], [TYPE, TABLET]], [
/droid\s[\d\.]+;\s(fp\du?)\sbuild/i
], [MODEL, [VENDOR, 'Fairphone'], [TYPE, MOBILE]], [
/\s(u304aa)\sbuild/i // AT&T
], [MODEL, [VENDOR, 'AT&T'], [TYPE, MOBILE]], [
/sie-(\w*)/i // Siemens
], [MODEL, [VENDOR, 'Siemens'], [TYPE, MOBILE]], [
/[;\/]\s?(rct\w+)\sbuild/i // RCA Tablets
], [MODEL, [VENDOR, 'RCA'], [TYPE, TABLET]], [
/[;\/\s](venue[\d\s]{2,7})\sbuild/i // Dell Venue Tablets
], [MODEL, [VENDOR, 'Dell'], [TYPE, TABLET]], [
/[;\/]\s?(q(?:mv|ta)\w+)\sbuild/i // Verizon Tablet
], [MODEL, [VENDOR, 'Verizon'], [TYPE, TABLET]], [
/[;\/]\s(?:barnes[&\s]+noble\s|bn[rt])([\w\s\+]*)\sbuild/i // Barnes & Noble Tablet
], [MODEL, [VENDOR, 'Barnes & Noble'], [TYPE, TABLET]], [
/[;\/]\s(tm\d{3}\w+)\sbuild/i
], [MODEL, [VENDOR, 'NuVision'], [TYPE, TABLET]], [
/;\s(k88)\sbuild/i // ZTE K Series Tablet
], [MODEL, [VENDOR, 'ZTE'], [TYPE, TABLET]], [
/;\s(nx\d{3}j)\sbuild/i // ZTE Nubia
], [MODEL, [VENDOR, 'ZTE'], [TYPE, MOBILE]], [
/[;\/]\s?(gen\d{3})\sbuild.*49h/i // Swiss GEN Mobile
], [MODEL, [VENDOR, 'Swiss'], [TYPE, MOBILE]], [
/[;\/]\s?(zur\d{3})\sbuild/i // Swiss ZUR Tablet
], [MODEL, [VENDOR, 'Swiss'], [TYPE, TABLET]], [
/[;\/]\s?((zeki)?tb.*\b)\sbuild/i // Zeki Tablets
], [MODEL, [VENDOR, 'Zeki'], [TYPE, TABLET]], [
/[;\/]\s([yr]\d{2})\sbuild/i,
/[;\/]\s(dragon[\-\s]+touch\s|dt)(\w{5})\sbuild/i // Dragon Touch Tablet
], [[VENDOR, 'Dragon Touch'], MODEL, [TYPE, TABLET]], [
/[;\/]\s?(ns-?\w{0,9})\sbuild/i // Insignia Tablets
], [MODEL, [VENDOR, 'Insignia'], [TYPE, TABLET]], [
/[;\/]\s?((nxa|Next)-?\w{0,9})\sbuild/i // NextBook Tablets
], [MODEL, [VENDOR, 'NextBook'], [TYPE, TABLET]], [
/[;\/]\s?(xtreme\_)?(v(1[045]|2[015]|[3469]0|7[05]))\sbuild/i
], [[VENDOR, 'Voice'], MODEL, [TYPE, MOBILE]], [ // Voice Xtreme Phones
/[;\/]\s?(lvtel\-)?(v1[12])\sbuild/i // LvTel Phones
], [[VENDOR, 'LvTel'], MODEL, [TYPE, MOBILE]], [
/;\s(ph-1)\s/i
], [MODEL, [VENDOR, 'Essential'], [TYPE, MOBILE]], [ // Essential PH-1
/[;\/]\s?(v(100md|700na|7011|917g).*\b)\sbuild/i // Envizen Tablets
], [MODEL, [VENDOR, 'Envizen'], [TYPE, TABLET]], [
/[;\/]\s?(trio[\s\w\-\.]+)\sbuild/i // MachSpeed Tablets
], [MODEL, [VENDOR, 'MachSpeed'], [TYPE, TABLET]], [
/[;\/]\s?tu_(1491)\sbuild/i // Rotor Tablets
], [MODEL, [VENDOR, 'Rotor'], [TYPE, TABLET]], [
/(shield[\w\s]+)\sbuild/i // Nvidia Shield Tablets
], [MODEL, [VENDOR, 'Nvidia'], [TYPE, TABLET]], [
/(sprint)\s(\w+)/i // Sprint Phones
], [VENDOR, MODEL, [TYPE, MOBILE]], [
/(kin\.[onetw]{3})/i // Microsoft Kin
], [[MODEL, /\./g, ' '], [VENDOR, 'Microsoft'], [TYPE, MOBILE]], [
/droid\s[\d\.]+;\s(cc6666?|et5[16]|mc[239][23]x?|vc8[03]x?)\)/i // Zebra
], [MODEL, [VENDOR, 'Zebra'], [TYPE, TABLET]], [
/droid\s[\d\.]+;\s(ec30|ps20|tc[2-8]\d[kx])\)/i
], [MODEL, [VENDOR, 'Zebra'], [TYPE, MOBILE]], [
///////////////////
// CONSOLES
///////////////////
/\s(ouya)\s/i, // Ouya
/(nintendo)\s([wids3utch]+)/i // Nintendo
], [VENDOR, MODEL, [TYPE, CONSOLE]], [
/droid.+;\s(shield)\sbuild/i // Nvidia
], [MODEL, [VENDOR, 'Nvidia'], [TYPE, CONSOLE]], [
/(playstation\s[345portablevi]+)/i // Playstation
], [MODEL, [VENDOR, 'Sony'], [TYPE, CONSOLE]], [
/[\s\(;](xbox(?:\sone)?(?!;\sxbox))[\s\);]/i // Microsoft Xbox
], [MODEL, [VENDOR, 'Microsoft'], [TYPE, CONSOLE]], [
///////////////////
// SMARTTVS
///////////////////
/smart-tv.+(samsung)/i // Samsung
], [VENDOR, [TYPE, SMARTTV]], [
/hbbtv.+maple;(\d+)/i
], [[MODEL, /^/, 'SmartTV'], [VENDOR, 'Samsung'], [TYPE, SMARTTV]], [
/(?:linux;\snetcast.+smarttv|lg\snetcast\.tv-201\d)/i, // LG SmartTV
], [[VENDOR, 'LG'], [TYPE, SMARTTV]], [
/(apple)\s?tv/i // Apple TV
], [VENDOR, [MODEL, 'Apple TV'], [TYPE, SMARTTV]], [
/crkey/i // Google Chromecast
], [[MODEL, 'Chromecast'], [VENDOR, 'Google'], [TYPE, SMARTTV]], [
/droid.+aft([\w])(\sbuild\/|\))/i // Fire TV
], [MODEL, [VENDOR, 'Amazon'], [TYPE, SMARTTV]], [
/\(dtv[\);].+(aquos)/i // Sharp
], [MODEL, [VENDOR, 'Sharp'], [TYPE, SMARTTV]], [
/hbbtv\/\d+\.\d+\.\d+\s+\([\w\s]*;\s*(\w[^;]*);([^;]*)/i // HbbTV devices
], [[VENDOR, util.trim], [MODEL, util.trim], [TYPE, SMARTTV]], [
/[\s\/\(](android\s|smart[-\s]?|opera\s)tv[;\)\s]/i // SmartTV from Unidentified Vendors
], [[TYPE, SMARTTV]], [
///////////////////
// WEARABLES
///////////////////
/((pebble))app\/[\d\.]+\s/i // Pebble
], [VENDOR, MODEL, [TYPE, WEARABLE]], [
/droid.+;\s(glass)\s\d/i // Google Glass
], [MODEL, [VENDOR, 'Google'], [TYPE, WEARABLE]], [
/droid\s[\d\.]+;\s(wt63?0{2,3})\)/i
], [MODEL, [VENDOR, 'Zebra'], [TYPE, WEARABLE]], [
///////////////////
// EMBEDDED
///////////////////
/(tesla)(?:\sqtcarbrowser|\/20[12]\d\.[\w\.-]+)/i // Tesla
], [VENDOR, [TYPE, EMBEDDED]], [
////////////////////
// MIXED (GENERIC)
///////////////////
/droid .+?; ([^;]+?)(?: build|\) applewebkit).+? mobile safari/i // Android Phones from Unidentified Vendors
], [MODEL, [TYPE, MOBILE]], [
/droid .+?;\s([^;]+?)(?: build|\) applewebkit).+?(?! mobile) safari/i // Android Tablets from Unidentified Vendors
], [MODEL, [TYPE, TABLET]], [
/\s(tablet|tab)[;\/]/i, // Unidentifiable Tablet
/\s(mobile)(?:[;\/]|\ssafari)/i // Unidentifiable Mobile
], [[TYPE, util.lowerize]], [
/(android[\w\.\s\-]{0,9});.+build/i // Generic Android Device
], [MODEL, [VENDOR, 'Generic']], [
/(phone)/i
], [[TYPE, MOBILE]]
],
engine : [[
/windows.+\sedge\/([\w\.]+)/i // EdgeHTML
], [VERSION, [NAME, 'EdgeHTML']], [
/webkit\/537\.36.+chrome\/(?!27)([\w\.]+)/i // Blink
], [VERSION, [NAME, 'Blink']], [
/(presto)\/([\w\.]+)/i, // Presto
/(webkit|trident|netfront|netsurf|amaya|lynx|w3m|goanna)\/([\w\.]+)/i, // WebKit/Trident/NetFront/NetSurf/Amaya/Lynx/w3m/Goanna
/ekioh(flow)\/([\w\.]+)/i, // Flow
/(khtml|tasman|links)[\/\s]\(?([\w\.]+)/i, // KHTML/Tasman/Links
/(icab)[\/\s]([23]\.[\d\.]+)/i // iCab
], [NAME, VERSION], [
/rv\:([\w\.]{1,9})\b.+(gecko)/i // Gecko
], [VERSION, NAME]
],
os : [[
// Windows
/microsoft\s(windows)\s(vista|xp)/i // Windows (iTunes)
], [NAME, VERSION], [
/(windows)\snt\s6\.2;\s(arm)/i, // Windows RT
/(windows\sphone(?:\sos)*)[\s\/]?([\d\.\s\w]*)/i, // Windows Phone
/(windows\smobile|windows)[\s\/]?([ntce\d\.\s]+\w)(?!.+xbox)/i
], [NAME, [VERSION, mapper.str, maps.os.windows.version]], [
/(win(?=3|9|n)|win\s9x\s)([nt\d\.]+)/i
], [[NAME, 'Windows'], [VERSION, mapper.str, maps.os.windows.version]], [
// iOS/macOS
/ip[honead]{2,4}\b(?:.*os\s([\w]+)\slike\smac|;\sopera)/i, // iOS
/cfnetwork\/.+darwin/i
], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [
/(mac\sos\sx)\s?([\w\s\.]*)/i,
/(macintosh|mac(?=_powerpc)\s)(?!.+haiku)/i // Mac OS
], [[NAME, 'Mac OS'], [VERSION, /_/g, '.']], [
// Mobile OSes // Android/WebOS/Palm/QNX/Bada/RIM/MeeGo/Contiki/Sailfish OS
/(android|webos|palm\sos|qnx|bada|rim\stablet\sos|meego|sailfish|contiki)[\/\s-]?([\w\.]*)/i,
/(blackberry)\w*\/([\w\.]*)/i, // Blackberry
/(tizen|kaios)[\/\s]([\w\.]+)/i, // Tizen/KaiOS
/\((series40);/i // Series 40
], [NAME, VERSION], [
/\(bb(10);/i // BlackBerry 10
], [VERSION, [NAME, 'BlackBerry']], [
/(?:symbian\s?os|symbos|s60(?=;)|series60)[\/\s-]?([\w\.]*)/i // Symbian
], [VERSION, [NAME, 'Symbian']], [
/mozilla.+\(mobile;.+gecko.+firefox/i // Firefox OS
], [[NAME, 'Firefox OS']], [
/web0s;.+rt(tv)/i,
/\b(?:hp)?wos(?:browser)?\/([\w\.]+)/i // WebOS
], [VERSION, [NAME, 'webOS']], [
// Google Chromecast
/crkey\/([\d\.]+)/i // Google Chromecast
], [VERSION, [NAME, 'Chromecast']], [
/(cros)\s[\w]+\s([\w\.]+\w)/i // Chromium OS
], [[NAME, 'Chromium OS'], VERSION],[
// Console
/(nintendo|playstation)\s([wids345portablevuch]+)/i, // Nintendo/Playstation
/(xbox);\s+xbox\s([^\);]+)/i, // Microsoft Xbox (360, One, X, S, Series X, Series S)
// GNU/Linux based
/(mint)[\/\s\(\)]?(\w*)/i, // Mint
/(mageia|vectorlinux)[;\s]/i, // Mageia/VectorLinux
/(joli|[kxln]?ubuntu|debian|suse|opensuse|gentoo|arch(?=\slinux)|slackware|fedora|mandriva|centos|pclinuxos|redhat|zenwalk|linpus|raspbian)(?:\sgnu\/linux)?(?:\slinux)?[\/\s-]?(?!chrom|package)([\w\.-]*)/i,
// Joli/Ubuntu/Debian/SUSE/Gentoo/Arch/Slackware
// Fedora/Mandriva/CentOS/PCLinuxOS/RedHat/Zenwalk/Linpus
/(hurd|linux)\s?([\w\.]*)/i, // Hurd/Linux
/(gnu)\s?([\w\.]*)/i, // GNU
// BSD based
/\s([frentopc-]{0,4}bsd|dragonfly)\s?(?!amd|[ix346]{1,2}86)([\w\.]*)/i, // FreeBSD/NetBSD/OpenBSD/PC-BSD/DragonFly
/(haiku)\s(\w+)/i // Haiku
], [NAME, VERSION], [
// Other
/(sunos)\s?([\w\.\d]*)/i // Solaris
], [[NAME, 'Solaris'], VERSION], [
/((?:open)?solaris)[\/\s-]?([\w\.]*)/i, // Solaris
/(aix)\s((\d)(?=\.|\)|\s)[\w\.])*/i, // AIX
/(plan\s9|minix|beos|os\/2|amigaos|morphos|risc\sos|openvms|fuchsia)/i, // Plan9/Minix/BeOS/OS2/AmigaOS/MorphOS/RISCOS/OpenVMS/Fuchsia
/(unix)\s?([\w\.]*)/i // UNIX
], [NAME, VERSION]
]
};
/////////////////
// Constructor
////////////////
var UAParser = function (ua, extensions) {
if (typeof ua === 'object') {
extensions = ua;
ua = undefined;
}
if (!(this instanceof UAParser)) {
return new UAParser(ua, extensions).getResult();
}
var _ua = ua || ((typeof window !== 'undefined' && window.navigator && window.navigator.userAgent) ? window.navigator.userAgent : EMPTY);
var _rgxmap = extensions ? util.extend(regexes, extensions) : regexes;
this.getBrowser = function () {
var _browser = { name: undefined, version: undefined };
mapper.rgx.call(_browser, _ua, _rgxmap.browser);
_browser.major = util.major(_browser.version); // deprecated
return _browser;
};
this.getCPU = function () {
var _cpu = { architecture: undefined };
mapper.rgx.call(_cpu, _ua, _rgxmap.cpu);
return _cpu;
};
this.getDevice = function () {
var _device = { vendor: undefined, model: undefined, type: undefined };
mapper.rgx.call(_device, _ua, _rgxmap.device);
return _device;
};
this.getEngine = function () {
var _engine = { name: undefined, version: undefined };
mapper.rgx.call(_engine, _ua, _rgxmap.engine);
return _engine;
};
this.getOS = function () {
var _os = { name: undefined, version: undefined };
mapper.rgx.call(_os, _ua, _rgxmap.os);
return _os;
};
this.getResult = function () {
return {
ua : this.getUA(),
browser : this.getBrowser(),
engine : this.getEngine(),
os : this.getOS(),
device : this.getDevice(),
cpu : this.getCPU()
};
};
this.getUA = function () {
return _ua;
};
this.setUA = function (ua) {
_ua = (typeof ua === STR_TYPE && ua.length > UA_MAX_LENGTH) ? util.trim(ua, UA_MAX_LENGTH) : ua;
return this;
};
this.setUA(_ua);
return this;
};
UAParser.VERSION = LIBVERSION;
UAParser.BROWSER = {
NAME : NAME,
MAJOR : MAJOR, // deprecated
VERSION : VERSION
};
UAParser.CPU = {
ARCHITECTURE : ARCHITECTURE
};
UAParser.DEVICE = {
MODEL : MODEL,
VENDOR : VENDOR,
TYPE : TYPE,
CONSOLE : CONSOLE,
MOBILE : MOBILE,
SMARTTV : SMARTTV,
TABLET : TABLET,
WEARABLE: WEARABLE,
EMBEDDED: EMBEDDED
};
UAParser.ENGINE = {
NAME : NAME,
VERSION : VERSION
};
UAParser.OS = {
NAME : NAME,
VERSION : VERSION
};
///////////
// Export
//////////
// check js environment
if (typeof(exports) !== UNDEF_TYPE) {
// nodejs env
if ("object" !== UNDEF_TYPE && module.exports) {
exports = module.exports = UAParser;
}
exports.UAParser = UAParser;
} else {
// requirejs env (optional)
if (true) {
!(__WEBPACK_AMD_DEFINE_RESULT__ = (function () {
return UAParser;
}).call(exports, __webpack_require__, exports, module),
__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
} else {}
}
// jQuery/Zepto specific (optional)
// Note:
// In AMD env the global scope should be kept clean, but jQuery is an exception.
// jQuery always exports to global scope, unless jQuery.noConflict(true) is used,
// and we should catch that.
var $ = typeof window !== 'undefined' && (window.jQuery || window.Zepto);
if ($ && !$.ua) {
var parser = new UAParser();
$.ua = parser.getResult();
$.ua.get = function () {
return parser.getUA();
};
$.ua.set = function (uastring) {
parser.setUA(uastring);
var result = parser.getResult();
for (var prop in result) {
$.ua[prop] = result[prop];
}
};
}
})(typeof window === 'object' ? window : this);
/***/ })
}]);
//# sourceMappingURL=ua-parser.chunk.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,158 @@
const v3_testItem = {
name: "Google",
username: "example@google.com",
password: "somethingsecret",
url: "https://google.com",
};
const v3_itemSearch = {
existing: "secret",
nonexistent: "apple",
};
describe("v3 compatibility", () => {
it("can signup without errors", () => {
// NOTE: Temporary, until Billing is implemented, because that throws an error
cy.on("uncaught:exception", (error) => {
// @ts-ignore this exists
if (error.code === "invalid_request") {
return false;
}
});
cy.v3_signup();
});
// it("can login without errors", () => {
// cy.v3_login();
// });
// it("can lock/unlock without errors", () => {
// cy.v3_login();
// cy.v3_lock();
// cy.v3_unlock();
// });
// it("can create an item without errors", () => {
// cy.v3_login();
// // Click plus sign
// cy.get("pl-app").find("pl-items").find("pl-items-list").find("pl-button:eq(2)").click();
// // Click create
// cy.get("pl-app").find("pl-create-item-dialog").find("footer pl-button.primary").click();
// cy.url().should("include", "/items/");
// cy.url().should("include", "/new");
// // Give the app some time to finish animations
// cy.wait(300);
// // Fill in form
// cy.get("pl-app")
// .find("pl-items")
// .find("pl-item-view")
// .find("pl-input#nameInput")
// .find("input")
// .type(v3_testItem.name, { force: true });
// cy.get("pl-app")
// .find("pl-items")
// .find("pl-item-view")
// .find("pl-scroller pl-list pl-field:eq(0)")
// .find("pl-input.value-input")
// .find("input.input-element")
// .type(v3_testItem.username, { force: true });
// cy.get("pl-app")
// .find("pl-items")
// .find("pl-item-view")
// .find("pl-scroller pl-list pl-field:eq(1)")
// .find("pl-input.value-input")
// .find("input.input-element")
// .type(v3_testItem.password, { force: true });
// cy.get("pl-app")
// .find("pl-items")
// .find("pl-item-view")
// .find("pl-scroller pl-list pl-field:eq(2)")
// .find("pl-input.value-input")
// .find("input.input-element")
// .type(v3_testItem.url, { force: true });
// // Click save
// cy.get("pl-app").find("pl-items").find("pl-item-view").find("pl-button.primary").click();
// cy.url().should("include", "/items/");
// cy.url().should("not.include", "/new");
// });
// it("can find an an item without errors", () => {
// cy.v3_unlock();
// // Click search sign
// cy.get("pl-app").find("pl-items").find("pl-items-list").find("pl-button:eq(3)").click();
// // Give the app some time to finish animations
// cy.wait(100);
// // Find Item
// cy.get("pl-app")
// .find("pl-items")
// .find("pl-items-list")
// .find("pl-input#filterInput")
// .find("input")
// .type(v3_itemSearch.existing, { force: true });
// // Confirm we only find one
// cy.get("pl-app")
// .find("pl-items")
// .find("pl-items-list")
// .find("main pl-virtual-list pl-scroller")
// .find("div.content")
// .children("div")
// .should("have.length", 1);
// // Confirm we find the right one
// cy.get("pl-app")
// .find("pl-items")
// .find("pl-items-list")
// .find("main pl-virtual-list pl-scroller")
// .find("div.content pl-vault-item-list-item")
// .find("div > div > div.semibold")
// .should("include.text", v3_testItem.name);
// // Click clear search sign
// cy.get("pl-app")
// .find("pl-items")
// .find("pl-items-list")
// .find("pl-input#filterInput")
// .find("pl-button.slim")
// .click();
// // Click search sign
// cy.get("pl-app").find("pl-items").find("pl-items-list").find("pl-button:eq(3)").click();
// // Find non-existent Item
// cy.get("pl-app")
// .find("pl-items")
// .find("pl-items-list")
// .find("pl-input#filterInput")
// .find("input")
// .type(v3_itemSearch.nonexistent, { force: true });
// // Confirm we find none
// cy.get("pl-app")
// .find("pl-items")
// .find("pl-items-list")
// .find("main pl-virtual-list pl-scroller")
// .find("div.content")
// .children("div")
// .should("have.length", 0);
// cy.get("pl-app")
// .find("pl-items")
// .find("pl-items-list")
// .find("main > div.centering")
// .should("contain.text", "did not match any items");
// });
});

View File

@ -18,7 +18,7 @@ Cypress.Commands.add("signup", () => {
cy.visit("/");
const { email, emailToken, password } = Cypress.env();
const { email, emailToken, password, name } = Cypress.env();
cy.get("pl-app").find("pl-start").find("pl-login-signup").find("pl-input#emailInput").find("input").type(email);
@ -53,7 +53,7 @@ Cypress.Commands.add("signup", () => {
.find("pl-drawer:eq(2)")
.find("pl-input")
.find("input")
.type("The Dude", { force: true });
.type(name, { force: true });
// Accept TOS
cy.get("pl-app")
@ -246,3 +246,190 @@ Cypress.Commands.add("unlock", () => {
cy.url().should("include", "/items");
});
Cypress.Commands.add("v3_signup", () => {
cy.clearCookies();
cy.clearLocalStorage();
cy.clearIndexedDb();
const { email, emailToken, password, v3_url, name } = Cypress.env();
cy.visit(`${v3_url}/`);
cy.get("pl-app").find("pl-start").find("pl-login").find("button.signup").click();
// Give the app some time to finish animations
cy.wait(100);
cy.get("pl-app")
.find("pl-start")
.find("pl-signup")
.find("pl-input#emailInput")
.find("input")
.type(email, { force: true });
cy.get("pl-app")
.find("pl-start")
.find("pl-signup")
.find("pl-input#nameInput")
.find("input")
.type(name, { force: true });
cy.get("pl-app")
.find("pl-start")
.find("pl-signup")
.find("pl-loading-button#submitEmailButton")
.click({ force: true });
// Give the app some time to finish animations
cy.wait(100);
cy.get("pl-app")
.find("pl-start")
.find("pl-signup")
.find("pl-input#codeInput")
.find("input")
.type(emailToken, { force: true });
cy.get("pl-app")
.find("pl-start")
.find("pl-signup")
.find("pl-loading-button#verifyEmailButton")
.click({ force: true });
// Give the app some time to finish animations
cy.wait(100);
// Choose a different password
cy.get("pl-app")
.find("pl-start")
.find("pl-signup")
.find("div.wrapper:eq(2) div.password-actions button:eq(1)")
.click({ force: true });
// Give the app some time to finish animations
cy.wait(100);
// Choose my own
cy.get("pl-app").find("pl-alert-dialog").find("button:eq(2)").click({ force: true });
// Give the app some time to finish animations
cy.wait(200);
// Type master password
cy.get("pl-app").find("pl-prompt-dialog").find("pl-input.tap").find("input").type(password, { force: true });
// Confirm master password
cy.get("pl-app").find("pl-prompt-dialog").find("pl-loading-button#confirmButton").click({ force: true });
// Give the app some time to render the alert, otherwise it sometimes shows out of place
cy.wait(200);
// Confirm weak password
cy.get("pl-app").find("pl-alert-dialog").find("button:eq(1)").click({ force: true });
// Give the app some time to finish animations
cy.wait(100);
// Repeat master password
cy.get("pl-app")
.find("pl-start")
.find("pl-signup")
.find("div.wrapper:eq(2) pl-password-input#repeatPasswordInput")
.find("input[type='password']")
.type(password, { force: true });
// Continue signup
cy.get("pl-app")
.find("pl-start")
.find("pl-signup")
.find("pl-loading-button#submitPasswordButton")
.click({ force: true });
// Wait for success
cy.url().should("include", "/signup/success");
// Done!
cy.get("pl-app")
.find("pl-start")
.find("pl-login-signup")
.find("pl-drawer:eq(7)")
.find("pl-button")
.click({ force: true });
cy.url().should("include", "/items");
});
Cypress.Commands.add("v3_login", () => {
// cy.clearCookies();
// cy.clearLocalStorage();
// cy.clearIndexedDb();
// cy.visit("/");
// const { email, emailToken, password } = Cypress.env();
// cy.get("pl-app").find("pl-start").find("pl-login-signup").find("pl-input#emailInput").find("input").type(email);
// cy.get("pl-app")
// .find("pl-start")
// .find("pl-login-signup")
// .find("pl-button#submitEmailButton")
// .click({ force: true });
// // Give the app some time to render the animations
// cy.wait(100);
// cy.get("pl-app")
// .find("pl-prompt-dialog")
// .find("pl-input")
// .find("input[placeholder='Enter Verification Code']")
// .type(emailToken, { force: true });
// cy.get("pl-app").find("pl-prompt-dialog").find("pl-button#confirmButton").click({ force: true });
// // Give the app some time to render the animations
// cy.wait(100);
// cy.get("pl-app")
// .find("pl-start")
// .find("pl-login-signup")
// .find("pl-drawer:eq(3)")
// .find("pl-password-input#loginPasswordInput")
// .find("input[type='password']")
// .type(password, { force: true });
// cy.get("pl-app")
// .find("pl-start")
// .find("pl-login-signup")
// .find("pl-drawer:eq(3)")
// .find("pl-button#loginButton")
// .click({ force: true });
// // Give the app some time to render the animations
// cy.wait(100);
// // Add trusted device
// cy.get("pl-app").find("pl-alert-dialog").find("pl-button:eq(0)").click({ force: true });
// cy.url().should("include", "/items");
});
Cypress.Commands.add("v3_lock", () => {
// cy.visit("/");
// // Open menu
// cy.get("pl-app").find("pl-items").find("pl-items-list").find("pl-button.menu-button:eq(0)").click({ force: true });
// // Click lock
// cy.get("pl-app").find("pl-menu").find("pl-button.menu-footer-button:eq(0)").click({ force: true });
// cy.url().should("include", "/unlock");
});
Cypress.Commands.add("v3_unlock", () => {
// cy.visit("/");
// const { email, password } = Cypress.env();
// // Give the app some time to render the animations
// cy.wait(100);
// cy.get("pl-app")
// .find("pl-start")
// .find("pl-unlock")
// .find("pl-input[label='Logged In As']")
// .find("input")
// .should("have.value", email);
// // Give the app some time to render the animations
// cy.wait(100);
// cy.get("pl-app")
// .find("pl-start")
// .find("pl-unlock")
// .find("pl-password-input#passwordInput")
// .find("input[type='password']")
// .type(password, { force: true });
// cy.get("pl-app").find("pl-start").find("pl-unlock").find("pl-button#unlockButton").click({ force: true });
// cy.url().should("include", "/items");
});

View File

@ -47,6 +47,30 @@ declare global {
* @example cy.unlock()
*/
unlock(): Chainable<Element>;
/**
* Custom command to run all the steps to signup in the v3 app.
* @example cy.v3_signup()
*/
v3_signup(): Chainable<Element>;
/**
* Custom command to run all the steps to login in the v3 app.
* @example cy.v3_login()
*/
v3_login(): Chainable<Element>;
/**
* Custom command to run all the steps to lock the v3 app.
* @example cy.v3_lock()
*/
v3_lock(): Chainable<Element>;
/**
* Custom command to run all the steps to unlock the v3 app.
* @example cy.v3_unlock()
*/
v3_unlock(): Chainable<Element>;
}
}
}

704
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -18,6 +18,7 @@
"devDependencies": {
"concurrently": "7.0.0",
"cypress": "9.3.1",
"http-server": "14.1.0",
"lerna": "4.0.0",
"prettier": "2.5.1",
"ts-node": "10.0.0",
@ -41,6 +42,7 @@
"cordova:build:ios": "lerna run build:ios",
"cordova:build:ios:signed": "lerna run build:ios:signed",
"start": "npm run pwa:build && lerna run --scope '@padloc/{server,pwa}' --parallel start",
"start:v3": "http-server cypress/fixtures/v3-client -s -p 8081 --proxy http://0.0.0.0:8081?",
"dev": "lerna run --parallel --scope '@padloc/{server,pwa}' --parallel dev",
"tauri:dev": "lerna run --parallel --scope '@padloc/{server,tauri}' --parallel dev",
"tauri:update": "lerna run update",
@ -48,8 +50,8 @@
"tauri:build:production": "lerna run build:production",
"repl": "cd packages/server && npm run repl && cd ../..",
"test": "lerna run test",
"test:e2e": "concurrently --prefix=name --prefix-length=30 --kill-others --success=first -n app,cypress \"PL_DATA_BACKEND=memory PL_E2E_TESTS=true PL_DISABLE_SW=true npm start\" \"./node_modules/.bin/wait-on tcp:localhost:8080 && CYPRESS_CRASH_REPORTS=0 cypress run\"",
"test:e2e:dev": "concurrently --prefix=name --prefix-length=30 --kill-others --success=first -n app,cypress \"PL_DATA_BACKEND=memory PL_E2E_TESTS=true PL_DISABLE_SW=true npm run dev\" \"./node_modules/.bin/wait-on tcp:localhost:8080 && CYPRESS_CRASH_REPORTS=0 cypress open\"",
"test:e2e": "concurrently --prefix=name --prefix-length=30 --kill-others --success=first -n app,v3-app,cypress \"PL_DATA_BACKEND=memory PL_E2E_TESTS=true PL_DISABLE_SW=true npm start\" \"npm run start:v3\" \"./node_modules/.bin/wait-on tcp:localhost:8080 && CYPRESS_CRASH_REPORTS=0 cypress run\"",
"test:e2e:dev": "concurrently --prefix=name --prefix-length=30 --kill-others --success=first -n app,v3-app,cypress \"PL_DATA_BACKEND=memory PL_E2E_TESTS=true PL_DISABLE_SW=true npm run dev\" \"npm run start:v3\" \"./node_modules/.bin/wait-on tcp:localhost:8080 && CYPRESS_CRASH_REPORTS=0 cypress open\"",
"locale:extract": "lerna run extract --scope '@padloc/locale'",
"add": "lerna add $1 --scope=@padloc/$scope",
"remove": "rm packages/$scope/package-lock.json && lerna exec \"npm uninstall $1\" --scope=@padloc/$scope",

View File

@ -139,7 +139,14 @@ export class RetrieveMFATokenResponse extends Serializable {
export class StartRegisterAuthenticatorParams extends Serializable {
type: AuthType = AuthType.Email;
purposes: AuthPurpose[] = [AuthPurpose.Signup, AuthPurpose.Login, AuthPurpose.Recover];
purposes: AuthPurpose[] = [
AuthPurpose.Signup,
AuthPurpose.Login,
AuthPurpose.Recover,
AuthPurpose.v3_Signup,
AuthPurpose.v3_Login,
AuthPurpose.v3_Recover,
];
data: any = {};

View File

@ -16,6 +16,9 @@ export enum AuthPurpose {
GetLegacyData = "get_legacy_data",
AccessKeyStore = "access_key_store",
TestAuthenticator = "test_authenticator",
v3_Signup = 0,
v3_Login = 1,
v3_Recover = 2,
}
export enum AuthType {

View File

@ -275,7 +275,13 @@ export class Controller extends API {
throw new Err(ErrorCode.AUTHENTICATION_TRIES_EXCEEDED, "You have exceed your allowed numer of tries!");
}
const authenticator = auth.authenticators.find((m) => m.id === request.authenticatorId);
const authenticators =
purpose === AuthPurpose.v3_Signup ||
(purpose === AuthPurpose.v3_Login && auth.authenticators.length === 0)
? await this._getAuthenticators(auth)
: auth.authenticators;
const authenticator = authenticators.find((m) => m.id === request.authenticatorId);
if (!authenticator) {
throw new Err(ErrorCode.AUTHENTICATION_FAILED, "Failed to complete auth request.");
}
@ -426,7 +432,7 @@ export class Controller extends API {
const authenticator = availableAuthenticators[authenticatorIndex || 0];
if (!authenticator) {
throw new Err(ErrorCode.NOT_FOUND, "No approriate authenticator found!");
throw new Err(ErrorCode.NOT_FOUND, "No appropriate authenticator found!");
}
const provider = this._getAuthServer(authenticator.type);
@ -457,7 +463,7 @@ export class Controller extends API {
deviceTrusted,
});
if (request.purpose === AuthPurpose.Login && deviceTrusted) {
if ((request.purpose === AuthPurpose.Login || request.purpose === AuthPurpose.v3_Login) && deviceTrusted) {
request.verified = new Date();
response.requestStatus = request.status = AuthRequestStatus.Verified;
response.accountStatus = auth.accountStatus;
@ -644,6 +650,10 @@ export class Controller extends API {
const auth = (this.context.auth = await this._getAuth(acc.email));
this.context.provisioning = await this.provisioner.getProvisioning(auth);
// TODO: Remove this
console.log("======== server.createSession");
console.log(JSON.stringify({ auth, srpId }));
// Get the pending SRP context for the given account
const srpState = auth.srpSessions.find((s) => s.id === srpId);
@ -1743,7 +1753,15 @@ export class Controller extends API {
}
private async _getAuthenticators(auth: Auth) {
const purposes = [AuthPurpose.Signup, AuthPurpose.Login, AuthPurpose.Recover, AuthPurpose.GetLegacyData];
const purposes = [
AuthPurpose.Signup,
AuthPurpose.Login,
AuthPurpose.Recover,
AuthPurpose.GetLegacyData,
AuthPurpose.v3_Signup,
AuthPurpose.v3_Login,
AuthPurpose.v3_Recover,
];
const adHocAuthenticators = await Promise.all(
this.config.defaultAuthTypes.map((type) => this._createAdHocAuthenticator(auth, purposes, type))
@ -1872,7 +1890,10 @@ export class Controller extends API {
(typeof requestId === "undefined" || r.id === requestId) &&
r.token === token &&
r.status === AuthRequestStatus.Verified &&
r.purpose === purpose
(r.purpose === purpose ||
(purpose === AuthPurpose.Signup && r.purpose === AuthPurpose.v3_Signup) ||
(purpose === AuthPurpose.Login && r.purpose === AuthPurpose.v3_Login) ||
(purpose === AuthPurpose.Recover && r.purpose === AuthPurpose.v3_Recover))
);
if (!request) {