@joystick.js/test-canary 0.0.0-canary.9 → 0.0.0-canary.91
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/_package.json +8 -1
- package/dist/helpers/databases/index.js +1 -1
- package/dist/helpers/databases/mongodb/availableQueryParameters.js +1 -0
- package/dist/helpers/databases/mongodb/buildConnectionString.js +1 -0
- package/dist/helpers/databases/mongodb/buildQueryParameters.js +1 -0
- package/dist/helpers/databases/mongodb/index.js +6 -0
- package/dist/helpers/databases/postgresql/addColumnToTable.js +1 -0
- package/dist/helpers/databases/postgresql/createAccountsIndexes.js +1 -0
- package/dist/helpers/databases/postgresql/createAccountsTables.js +1 -0
- package/dist/helpers/databases/postgresql/createDatabase.js +0 -0
- package/dist/helpers/databases/postgresql/index.js +11 -0
- package/dist/helpers/load/index.js +1 -1
- package/dist/helpers/queues/index.js +1 -1
- package/dist/helpers/render/event.js +1 -0
- package/dist/helpers/render/index.js +9 -0
- package/dist/index.js +1 -1
- package/dist/lib/{log.js → CLILog.js} +1 -1
- package/dist/lib/generateId.js +1 -0
- package/dist/lib/isValidJSONString.js +1 -0
- package/dist/lib/loadSettings.js +1 -0
- package/dist/lib/serializeQueryParameters.js +1 -0
- package/dist/test.js +1 -1
- package/package.json +1 -1
- package/src/helpers/databases/index.js +58 -1
- package/src/helpers/databases/mongodb/availableQueryParameters.js +39 -0
- package/src/helpers/databases/mongodb/buildConnectionString.js +28 -0
- package/src/helpers/databases/mongodb/buildQueryParameters.js +15 -0
- package/src/helpers/databases/mongodb/index.js +43 -0
- package/src/helpers/databases/postgresql/addColumnToTable.js +3 -0
- package/src/helpers/databases/postgresql/createAccountsIndexes.js +29 -0
- package/src/helpers/databases/postgresql/createAccountsTables.js +44 -0
- package/src/helpers/databases/postgresql/createDatabase.js +0 -0
- package/src/helpers/databases/postgresql/index.js +52 -0
- package/src/helpers/load/index.js +11 -6
- package/src/helpers/queues/index.js +9 -1
- package/src/helpers/render/event.js +10 -0
- package/src/helpers/render/index.js +92 -0
- package/src/index.js +11 -2
- package/src/lib/{log.js → CLILog.js} +1 -1
- package/src/lib/generateId.js +73 -0
- package/src/lib/isValidJSONString.js +7 -0
- package/src/lib/loadSettings.js +90 -0
- package/src/lib/serializeQueryParameters.js +5 -0
- package/src/test.js +69 -4
- package/dist/helpers/component/index.js +0 -1
- package/src/helpers/component/index.js +0 -1
package/_package.json
CHANGED
|
@@ -16,6 +16,13 @@
|
|
|
16
16
|
"license": "SAUCR",
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"ava": "^5.3.1",
|
|
19
|
-
"
|
|
19
|
+
"chalk": "^5.3.0",
|
|
20
|
+
"dayjs": "^1.11.9",
|
|
21
|
+
"esbuild": "^0.18.17",
|
|
22
|
+
"linkedom": "^0.15.1",
|
|
23
|
+
"mongo-uri-tool": "^1.0.1",
|
|
24
|
+
"mongodb": "^5.7.0",
|
|
25
|
+
"node-fetch": "^3.3.2",
|
|
26
|
+
"pg": "^8.11.2"
|
|
20
27
|
}
|
|
21
28
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var e={};export{
|
|
1
|
+
import n from"../../lib/loadSettings.js";import p from"./mongodb/index.js";import i from"./postgresql/index.js";var l=async()=>{const r=(await n({environment:"test"}))?.parsed?.config?.databases?.map(e=>({provider:e?.provider,settings:e}));for(let e=0;e<r?.length;e+=1){const t=r[e],a=r?.filter(s=>s?.provider===s?.provider)?.length>1,o=parseInt(process.env.PORT,10)+10+e;if(t?.provider==="mongodb"){const s=await p(t?.settings,o);process.databases={...process.databases||{},mongodb:a?{...process?.databases?.mongodb||{},[t?.settings?.name||`mongodb_${o}`]:s}:s}}if(t?.provider==="postgresql"){const s=await i(t?.settings,o);process.databases={...process.databases||{},postgresql:a?{...process?.databases?.postgresql||{},[t?.settings?.name||`postgresql_${o}`]:{...s?.pool,query:s?.query}}:{...s?.pool,query:s?.query}}}}return process.databases};export{l as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=["replicaSet","tls","ssl","tlsCertificateKeyFile","tlsCertificateKeyFilePassword","tlsCAFile","tlsAllowInvalidCertificates","tlsAllowInvalidHostnames","tlsInsecure","connectTimeoutMS","socketTimeoutMS","compressors","zlibCompressionLevel","maxPoolSize","minPoolSize","maxIdleTimeMS","waitQueueMultiple","waitQueueTimeoutMS","w","wtimeoutMS","journal","readConcernLevel","readPreference","maxStalenessSeconds","readPreferenceTags","authSource","authMechanism","authMechanismProperties","gssapiServiceName","localThresholdMS","serverSelectionTimeoutMS","serverSelectionTryOnce","heartbeatFrequencyMS","appName","retryReads","retryWrites","uuidRepresentation"];export{e as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import m from"./buildQueryParameters.js";import $ from"../../../lib/serializeQueryParameters.js";var p=(r={})=>{let s="mongodb://";r&&(r.username||r.password)&&(s=`${s}${r.username||r.password?`${r.username||""}${r.username&&r.password?":":""}${r.password||""}@`:""}`),r&&r.hosts&&Array.isArray(r.hosts)&&(s=`${s}${r.hosts.map(e=>`${e.hostname}:${e.port}`).join(",")}`),r&&r.database&&(s=`${s}/${r.database}`);const a=m(r);return Object.keys(a)?.length>0&&(s=`${s}?${$(a)}`),s};export{p as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import l from"./availableQueryParameters.js";var f=(r={})=>{const t={};for(let a=0;a<l.length;a+=1){const e=l[a];r&&r[e]&&(t[e]=r[e])}return t};export{f as default};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import{MongoClient as s}from"mongodb";import c from"chalk";import a from"mongo-uri-tool";import p from"fs";import l from"./buildConnectionString.js";var b=async(o={},e=2610)=>{const r=o?.connection||{hosts:[{hostname:"127.0.0.1",port:e}],database:"app",replicaSet:`joystick_${e}`},t=l(r),i=a.parseUri(t);try{const n={useNewUrlParser:!0,useUnifiedTopology:!0,ssl:!["development","test"].includes(process.env.NODE_ENV),...o?.options||{}};return o?.options?.ca&&(n.ca=p.readFileSync(o?.options?.ca)),(await s.connect(t,n)).db(i.db)}catch(n){console.warn(c.yellowBright(`
|
|
2
|
+
Failed to connect to MongoDB. Please double-check connection settings and try again.
|
|
3
|
+
|
|
4
|
+
Error from MongoDB:
|
|
5
|
+
|
|
6
|
+
${c.redBright(n?.message)}`))}};export{b as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var a=(e="",r="",s="")=>process.databases._users?.query(`ALTER TABLE ${e} ADD COLUMN ${r} ${s}`);export{a as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const a=async(s="",r="",_=[])=>process.databases._users?.query(`CREATE UNIQUE INDEX IF NOT EXISTS ${s} ON ${r}(${_.join(", ")})`),e=async(s="",r="",_=[])=>process.databases._users?.query(`CREATE INDEX IF NOT EXISTS ${s} ON ${r}(${_.join(", ")})`);var o=async()=>{await e("user_by_email","users",["email_address"]),await e("user_by_username","users",["username"]),await e("user_by_user_id","users",["user_id"]),await e("user_session_by_token","users_sessions",["token"]),await e("user_password_reset_token_by_token","users_password_reset_tokens",["token"]),await e("user_password_reset_token_by_user_id_token","users_password_reset_tokens",["user_id","token"]),await e("user_role","users_roles",["role"]),await e("user_roles_by_user_id_role","users_roles",["user_id","role"]),await e("role","roles",["role"]),await a("user_roles","users_roles",["user_id","role"])};export{o as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=async(t="",s=[])=>process.databases._users?.query(`CREATE TABLE IF NOT EXISTS ${t} (${s.join(", ")})`);var r=async()=>{await e("users",["id bigserial primary key","user_id text","email_address text","password text","username text","language text"]),await e("users_sessions",["id bigserial primary key","user_id text","token text","token_expires_at text"]),await e("users_password_reset_tokens",["id bigserial primary key","user_id text","token text","requested_at text"]),await e("users_verify_email_tokens",["id bigserial primary key","user_id text","token text"]),await e("roles",["id bigserial primary key","role text"]),await e("users_roles",["id bigserial primary key","user_id text","role text"])};export{r as default};
|
|
File without changes
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import l from"pg";import n from"chalk";import h from"os";const{Pool:i}=l;var u=async(r={},c=2610)=>{const o=r?.connection||{hosts:[{hostname:"127.0.0.1",port:c}],database:"app",username:(h.userInfo()||{}).username||"",password:""};try{const e=o.hosts&&o.hosts[0],a=new i({user:o?.username||"",database:o?.database,password:o?.password||"",host:e?.hostname,port:e?.port,...r?.options||{}});return{pool:a,query:(...t)=>a.query(...t).then(s=>s?.rows||[]).catch(s=>{throw console.log(n.redBright(`
|
|
2
|
+
Failed SQL Statement:
|
|
3
|
+
`)),console.log(t[0]),console.log(`
|
|
4
|
+
`),console.log(n.redBright(`
|
|
5
|
+
Failed Values:
|
|
6
|
+
`)),console.log(t[1]),s})}}catch(e){console.warn(n.yellowBright(`
|
|
7
|
+
Failed to connect to PostgreSQL. Please double-check connection settings and try again.
|
|
8
|
+
|
|
9
|
+
Error from PostgreSQL:
|
|
10
|
+
|
|
11
|
+
${n.redBright(e?.message)}`))}};export{u as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import n from"fs";import a from"../../lib/CLILog.js";const c=async(o="",s={})=>{const t=await import(`${o}?update=${Date.now()}`);return t?.default&&s?.default?t.default:t};var u=async(o="",s={})=>{const e=o?.charAt(0)==="/"?o.substring(1,o.length):o,t=`${process.cwd()}/.joystick/build/${e}`;return n.existsSync(t)?c(t,s):(a(`[test.load] Path at ${t} not found.`,{level:"warning",docs:"https://cheatcode.co/docs/joystick/test/load"}),null)};export{u as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var e={};export{
|
|
1
|
+
var u={job:(e="",o={})=>{}};export{u as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var o=(e="",t="",n={})=>{const p=n?.document?.querySelector("#app")?.querySelector(t),r=new Event(e);return p.dispatchEvent(r),!0};export{o as default};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import{parseHTML as l}from"linkedom";import d from"@joystick.js/ui-canary";import i from"node-fetch";import{URL as w,URLSearchParams as m}from"url";import p from"./event.js";import h from"../load/index.js";const u=async(e="")=>{const r=new w(`${window?.location?.origin}/api/_test/bootstrap`);r.search=new m({pathToComponent:e});const o=await i(r).then(async a=>a.json());window.joystick={},window.joystick.settings={},window.__joystick_data__=o?.data||{},window.__joystick_i18n__=o?.translations||{},window.__joystick_req__=o?.req},_=()=>{const e=l(`
|
|
2
|
+
<html>
|
|
3
|
+
<head></head>
|
|
4
|
+
<body>
|
|
5
|
+
<div id="app"></div>
|
|
6
|
+
<meta name="csrf" content="joystick_test" />
|
|
7
|
+
</body>
|
|
8
|
+
</html>
|
|
9
|
+
`),{window:r,document:o,Element:a,Event:n,HTMLElement:t}=e;return global.window=r,global.document=o,global.HTMLElement=t,global.Element=a,global.Event=n,global.console={log:console.log,warn:console.warn,error:console.error},e};var k=async(e="",r={})=>{const o=_();window.fetch=i,window.location={origin:`http://localhost:${process.env.PORT}`},await u(e);const a=await h(e,{default:!0}),n=d.mount(a,r?.props||{},o?.document.querySelector("#app"));return n.isTest=!0,{dom:o,instance:n,test:{data:async(t={})=>n?.data?.refetch(t),renderToHTML:()=>{const t=n?.renderToHTML(),s=new RegExp("<when>|</when>","g");return t?.wrapped?.replace(s,"")?.replace(/\n|\t/g," ")?.replace(/> *</g,"><")},method:(t="",...s)=>{const c=n?.methods[t];return c?c(...s):null},event:(t="",s="")=>p(t,s,o)}}};export{k as default};
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import r from"./test.js";import e from"./helpers/api/index.js";import o from"./helpers/render/index.js";import t from"./helpers/databases/index.js";import m from"./helpers/email/index.js";import a from"./helpers/queues/index.js";import f from"./helpers/load/index.js";import p from"./helpers/routes/index.js";import i from"./helpers/uploaders/index.js";import s from"./helpers/websockets/index.js";var k={accounts:{signup:r.signupUser,delete:r.deleteUser},after:r.after,afterEach:r.afterEach,api:e,before:r.before,beforeEach:r.beforeEach,render:o,databases:t,email:m,load:f,queues:a,routes:p,that:r.that,uploaders:i,websockets:s};export{k as default};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import l from"chalk";import o from"./rainbowRoad.js";var t=(c="",e={})=>{const
|
|
1
|
+
import l from"chalk";import o from"./rainbowRoad.js";var t=(c="",e={})=>{const g={info:"blue",success:"green",warning:"yellowBright",danger:"red"},d={info:"\u2771 Info",success:"\u2771 Ok",warning:"\u2771 Warning",danger:"\u2771 Error"},a=e.level?g[e.level]:"gray",r=e.level?d[e.level]:"Log",$=e.docs||"https://github.com/cheatcode/joystick";console.log(`
|
|
2
2
|
${e.padding||""}${o()}
|
|
3
3
|
`),console.log(`${e.padding||""}${l[a](`${r}:`)}
|
|
4
4
|
`),console.log(`${e.padding||""}${l.white(c)}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var l=(e=16)=>{let t=[],a=["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"];for(let r=0;r<e;r++)t.push(a[Math.floor(Math.random()*16)]);return t.join("")};export{l as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var t=(r="")=>{try{return JSON.parse(r)}catch{return!1}};export{t as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import r from"fs";import i from"./CLILog.js";import c from"./isValidJSONString.js";const a=(e="")=>{try{const t=c(e),s=process.env.NODE_ENV==="test"?"test":"start";t||(i(`Failed to parse settings file. Double-check the syntax in your settings.${process.env.NODE_ENV}.json file at the root of your project and rerun joystick ${s}.`,{level:"danger",docs:"https://cheatcode.co/docs/joystick/environment-settings",tools:[{title:"JSON Linter",url:"https://jsonlint.com/"}]}),process.exit(0))}catch(t){throw new Error(`[loadSettings.warnIfInvalidJSONInSettings] ${t.message}`)}},d=(e="")=>{try{return r.readFileSync(e,"utf-8")}catch(t){throw new Error(`[loadSettings.getSettings] ${t.message}`)}},g=(e="")=>{try{const t=r.existsSync(e),s=process.env.NODE_ENV==="test"?"test":"start";t||(i(`A settings file could not be found for this environment (${process.env.NODE_ENV}). Create a settings.${process.env.NODE_ENV}.json file at the root of your project and rerun joystick ${s}.`,{level:"danger",docs:`https://cheatcode.co/docs/joystick/cli/${s}`}),process.exit(0))}catch(t){throw new Error(`[loadSettings.warnIfSettingsNotFound] ${t.message}`)}},l=e=>{try{if(!e)throw new Error("options object is required.");if(!e.environment)throw new Error("options.environment is required.")}catch(t){throw new Error(`[loadSettings.validateOptions] ${t.message}`)}},p=(e,{resolve:t,reject:s})=>{try{l(e);const o=`${process.cwd()}/settings.${e.environment}.json`;g(o);const n=d(o);a(n),process.env.JOYSTICK_SETTINGS=n,t({parsed:JSON.parse(n),unparsed:n})}catch(o){s(`[loadSettings] ${o.message}`)}};var N=e=>new Promise((t,s)=>{p(e,{resolve:t,reject:s})});export{N as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var n=(e={})=>Object.entries(e).map(([r,t])=>`${r}=${t}`)?.join("&");export{n as default};
|
package/dist/test.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import t from"ava";import a from"node-fetch";let n=[];class u{constructor(){}async signupUser(e=[]){for(let r=0;r<e?.length;r+=1){const l=e[r],c=await a(`http://localhost:${process.env.PORT}/api/_test/accounts/signup`,{method:"POST",mode:"cors",headers:{"Content-Type":"application/json"},body:JSON.stringify(l)}).then(i=>i.json());n.push(c)}}deleteUser(e=""){return a(`http://localhost:${process.env.PORT}/api/_test/accounts`,{method:"DELETE",mode:"cors",headers:{"Content-Type":"application/json"},body:JSON.stringify({userId:e})})}before(e=null){return t.serial.before(e)}beforeEach(e=null){return t.beforeEach(e)}after(e=null){return t.after.always(e)}afterEach(e=null){return t.afterEach.always(e)}that(e="",r=null){return t.serial(e,r)}}const o=new u;t.serial.after.always(async()=>{for(let s=0;s<n?.length;s+=1){const e=n[s];await o.deleteUser(e?._id||e?.user_id)}});var f=o;export{f as default};
|
package/package.json
CHANGED
|
@@ -1 +1,58 @@
|
|
|
1
|
-
|
|
1
|
+
import loadSettings from "../../lib/loadSettings.js";
|
|
2
|
+
import connectMongoDB from './mongodb/index.js';
|
|
3
|
+
import connectPostgreSQL from './postgresql/index.js';
|
|
4
|
+
|
|
5
|
+
export default async () => {
|
|
6
|
+
const settings = (await loadSettings({ environment: 'test' }))?.parsed;
|
|
7
|
+
|
|
8
|
+
const databases = settings?.config?.databases?.map((database) => {
|
|
9
|
+
return {
|
|
10
|
+
provider: database?.provider,
|
|
11
|
+
settings: database,
|
|
12
|
+
};
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
for (
|
|
16
|
+
let databaseIndex = 0;
|
|
17
|
+
databaseIndex < databases?.length;
|
|
18
|
+
databaseIndex += 1
|
|
19
|
+
) {
|
|
20
|
+
const database = databases[databaseIndex];
|
|
21
|
+
const hasMultipleOfProvider = (databases?.filter((database) => database?.provider === database?.provider))?.length > 1;
|
|
22
|
+
const databasePort = parseInt(process.env.PORT, 10) + 10 + databaseIndex;
|
|
23
|
+
|
|
24
|
+
if (database?.provider === "mongodb") {
|
|
25
|
+
const mongodb = await connectMongoDB(database?.settings, databasePort);
|
|
26
|
+
process.databases = {
|
|
27
|
+
...(process.databases || {}),
|
|
28
|
+
mongodb: !hasMultipleOfProvider ? mongodb : {
|
|
29
|
+
...(process?.databases?.mongodb || {}),
|
|
30
|
+
[database?.settings?.name || `mongodb_${databasePort}`]: mongodb,
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (database?.provider === "postgresql") {
|
|
36
|
+
const postgresql = await connectPostgreSQL(
|
|
37
|
+
database?.settings,
|
|
38
|
+
databasePort
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
process.databases = {
|
|
42
|
+
...(process.databases || {}),
|
|
43
|
+
postgresql: !hasMultipleOfProvider ? {
|
|
44
|
+
...postgresql?.pool,
|
|
45
|
+
query: postgresql?.query,
|
|
46
|
+
} : {
|
|
47
|
+
...(process?.databases?.postgresql || {}),
|
|
48
|
+
[database?.settings?.name || `postgresql_${databasePort}`]: {
|
|
49
|
+
...postgresql?.pool,
|
|
50
|
+
query: postgresql?.query,
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return process.databases;
|
|
58
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export default [
|
|
2
|
+
"replicaSet",
|
|
3
|
+
"tls",
|
|
4
|
+
"ssl",
|
|
5
|
+
"tlsCertificateKeyFile",
|
|
6
|
+
"tlsCertificateKeyFilePassword",
|
|
7
|
+
"tlsCAFile",
|
|
8
|
+
"tlsAllowInvalidCertificates",
|
|
9
|
+
"tlsAllowInvalidHostnames",
|
|
10
|
+
"tlsInsecure",
|
|
11
|
+
"connectTimeoutMS",
|
|
12
|
+
"socketTimeoutMS",
|
|
13
|
+
"compressors",
|
|
14
|
+
"zlibCompressionLevel",
|
|
15
|
+
"maxPoolSize",
|
|
16
|
+
"minPoolSize",
|
|
17
|
+
"maxIdleTimeMS",
|
|
18
|
+
"waitQueueMultiple",
|
|
19
|
+
"waitQueueTimeoutMS",
|
|
20
|
+
"w",
|
|
21
|
+
"wtimeoutMS",
|
|
22
|
+
"journal",
|
|
23
|
+
"readConcernLevel",
|
|
24
|
+
"readPreference",
|
|
25
|
+
"maxStalenessSeconds",
|
|
26
|
+
"readPreferenceTags",
|
|
27
|
+
"authSource",
|
|
28
|
+
"authMechanism",
|
|
29
|
+
"authMechanismProperties",
|
|
30
|
+
"gssapiServiceName",
|
|
31
|
+
"localThresholdMS",
|
|
32
|
+
"serverSelectionTimeoutMS",
|
|
33
|
+
"serverSelectionTryOnce",
|
|
34
|
+
"heartbeatFrequencyMS",
|
|
35
|
+
"appName",
|
|
36
|
+
"retryReads",
|
|
37
|
+
"retryWrites",
|
|
38
|
+
"uuidRepresentation"
|
|
39
|
+
];
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import buildQueryParameters from "./buildQueryParameters.js";
|
|
2
|
+
import serializeQueryParameters from "../../../lib/serializeQueryParameters.js";
|
|
3
|
+
|
|
4
|
+
export default (connection = {}) => {
|
|
5
|
+
let connectionString = "mongodb://";
|
|
6
|
+
|
|
7
|
+
if (connection && (connection.username || connection.password)) {
|
|
8
|
+
connectionString = `${connectionString}${connection.username || connection.password ? `${connection.username || ""}${!!connection.username && !!connection.password ? ':' : ''}${connection.password || ""}@` : ''}`;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (connection && connection.hosts && Array.isArray(connection.hosts)) {
|
|
12
|
+
connectionString = `${connectionString}${connection.hosts
|
|
13
|
+
.map((host) => `${host.hostname}:${host.port}`)
|
|
14
|
+
.join(",")}`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (connection && connection.database) {
|
|
18
|
+
connectionString = `${connectionString}/${connection.database}`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const queryParameters = buildQueryParameters(connection);
|
|
22
|
+
|
|
23
|
+
if (Object.keys(queryParameters)?.length > 0 ) {
|
|
24
|
+
connectionString = `${connectionString}?${serializeQueryParameters(queryParameters)}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return connectionString;
|
|
28
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import availableQueryParameters from "./availableQueryParameters.js";
|
|
2
|
+
|
|
3
|
+
export default (connection = {}) => {
|
|
4
|
+
const queryParameters = {};
|
|
5
|
+
|
|
6
|
+
for (let i = 0; i < availableQueryParameters.length; i += 1) {
|
|
7
|
+
const availableParameter = availableQueryParameters[i];
|
|
8
|
+
|
|
9
|
+
if (connection && connection[availableParameter]) {
|
|
10
|
+
queryParameters[availableParameter] = connection[availableParameter];
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return queryParameters;
|
|
15
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { MongoClient } from "mongodb";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import mongoUri from "mongo-uri-tool";
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import buildConnectionString from "./buildConnectionString.js";
|
|
6
|
+
|
|
7
|
+
export default async (settings = {}, databasePort = 2610) => {
|
|
8
|
+
const connection = settings?.connection || {
|
|
9
|
+
hosts: [
|
|
10
|
+
// NOTE: By default, expect databases start from 2610 (assuming a PORT of 2600).
|
|
11
|
+
{ hostname: "127.0.0.1", port: databasePort },
|
|
12
|
+
],
|
|
13
|
+
database: "app",
|
|
14
|
+
replicaSet: `joystick_${databasePort}`,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const connectionString = buildConnectionString(connection);
|
|
18
|
+
const parsedURI = mongoUri.parseUri(connectionString);
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const connectionOptions = {
|
|
22
|
+
useNewUrlParser: true,
|
|
23
|
+
useUnifiedTopology: true,
|
|
24
|
+
ssl: !['development', 'test'].includes(process.env.NODE_ENV),
|
|
25
|
+
...(settings?.options || {})
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
if (settings?.options?.ca) {
|
|
29
|
+
connectionOptions.ca = fs.readFileSync(settings?.options?.ca);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const client = await MongoClient.connect(connectionString, connectionOptions);
|
|
33
|
+
const db = client.db(parsedURI.db);
|
|
34
|
+
|
|
35
|
+
return db;
|
|
36
|
+
} catch (exception) {
|
|
37
|
+
console.warn(
|
|
38
|
+
chalk.yellowBright(
|
|
39
|
+
`\nFailed to connect to MongoDB. Please double-check connection settings and try again.\n\nError from MongoDB:\n\n${chalk.redBright(exception?.message)}`
|
|
40
|
+
),
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const createUniqueIndex = async (indexName = '', tableName = '', tableColumns = []) => {
|
|
2
|
+
return process.databases._users?.query(`CREATE UNIQUE INDEX IF NOT EXISTS ${indexName} ON ${tableName}(${tableColumns.join(', ')})`);
|
|
3
|
+
};
|
|
4
|
+
|
|
5
|
+
const createIndex = async (indexName = '', tableName = '', tableColumns = []) => {
|
|
6
|
+
return process.databases._users?.query(`CREATE INDEX IF NOT EXISTS ${indexName} ON ${tableName}(${tableColumns.join(', ')})`);
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export default async () => {
|
|
10
|
+
// users
|
|
11
|
+
await createIndex('user_by_email', 'users', ['email_address']);
|
|
12
|
+
await createIndex('user_by_username', 'users', ['username']);
|
|
13
|
+
await createIndex('user_by_user_id', 'users', ['user_id']);
|
|
14
|
+
|
|
15
|
+
// users_sessions
|
|
16
|
+
await createIndex('user_session_by_token', 'users_sessions', ['token']);
|
|
17
|
+
|
|
18
|
+
// users_password_reset_tokens
|
|
19
|
+
await createIndex('user_password_reset_token_by_token', 'users_password_reset_tokens', ['token']);
|
|
20
|
+
await createIndex('user_password_reset_token_by_user_id_token', 'users_password_reset_tokens', ['user_id', 'token']);
|
|
21
|
+
|
|
22
|
+
// users_roles
|
|
23
|
+
await createIndex('user_role', 'users_roles', ['role']);
|
|
24
|
+
await createIndex('user_roles_by_user_id_role', 'users_roles', ['user_id', 'role']);
|
|
25
|
+
|
|
26
|
+
// roles
|
|
27
|
+
await createIndex('role', 'roles', ['role']);
|
|
28
|
+
await createUniqueIndex('user_roles', 'users_roles', ['user_id', 'role']);
|
|
29
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const createTable = async (table = "", tableColumns = []) => {
|
|
2
|
+
return process.databases._users?.query(
|
|
3
|
+
`CREATE TABLE IF NOT EXISTS ${table} (${tableColumns.join(", ")})`
|
|
4
|
+
);
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export default async () => {
|
|
8
|
+
await createTable("users", [
|
|
9
|
+
"id bigserial primary key",
|
|
10
|
+
"user_id text",
|
|
11
|
+
"email_address text",
|
|
12
|
+
"password text",
|
|
13
|
+
"username text",
|
|
14
|
+
"language text",
|
|
15
|
+
]);
|
|
16
|
+
|
|
17
|
+
await createTable("users_sessions", [
|
|
18
|
+
"id bigserial primary key",
|
|
19
|
+
"user_id text",
|
|
20
|
+
"token text",
|
|
21
|
+
"token_expires_at text",
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
await createTable("users_password_reset_tokens", [
|
|
25
|
+
"id bigserial primary key",
|
|
26
|
+
"user_id text",
|
|
27
|
+
"token text",
|
|
28
|
+
"requested_at text",
|
|
29
|
+
]);
|
|
30
|
+
|
|
31
|
+
await createTable("users_verify_email_tokens", [
|
|
32
|
+
"id bigserial primary key",
|
|
33
|
+
"user_id text",
|
|
34
|
+
"token text",
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
await createTable("roles", ["id bigserial primary key", "role text"]);
|
|
38
|
+
|
|
39
|
+
await createTable("users_roles", [
|
|
40
|
+
"id bigserial primary key",
|
|
41
|
+
"user_id text",
|
|
42
|
+
"role text",
|
|
43
|
+
]);
|
|
44
|
+
};
|
|
File without changes
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import postgresql from 'pg';
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import os from 'os';
|
|
4
|
+
|
|
5
|
+
const { Pool } = postgresql;
|
|
6
|
+
|
|
7
|
+
export default async (settings = {}, databasePort = 2610) => {
|
|
8
|
+
const connection = settings?.connection || {
|
|
9
|
+
hosts: [
|
|
10
|
+
{ hostname: "127.0.0.1", port: databasePort },
|
|
11
|
+
],
|
|
12
|
+
database: "app",
|
|
13
|
+
// NOTE: PostgreSQL creates a default superuser based on the OS username.
|
|
14
|
+
username: (os.userInfo() || {}).username || "",
|
|
15
|
+
password: "",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
const host = connection.hosts && connection.hosts[0];
|
|
20
|
+
const pool = new Pool({
|
|
21
|
+
user: connection?.username || '',
|
|
22
|
+
database: connection?.database,
|
|
23
|
+
password: connection?.password || '',
|
|
24
|
+
host: host?.hostname,
|
|
25
|
+
port: host?.port,
|
|
26
|
+
...(settings?.options || {})
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
pool,
|
|
31
|
+
query: (...args) => {
|
|
32
|
+
return pool.query(...args).then((response) => {
|
|
33
|
+
return response?.rows || [];
|
|
34
|
+
}).catch((error) => {
|
|
35
|
+
console.log(chalk.redBright(`\nFailed SQL Statement:\n`));
|
|
36
|
+
console.log(args[0]);
|
|
37
|
+
console.log(`\n`);
|
|
38
|
+
console.log(chalk.redBright(`\nFailed Values:\n`));
|
|
39
|
+
console.log(args[1]);
|
|
40
|
+
|
|
41
|
+
throw error;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
} catch (exception) {
|
|
46
|
+
console.warn(
|
|
47
|
+
chalk.yellowBright(
|
|
48
|
+
`\nFailed to connect to PostgreSQL. Please double-check connection settings and try again.\n\nError from PostgreSQL:\n\n${chalk.redBright(exception?.message)}`
|
|
49
|
+
),
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
|
-
import
|
|
2
|
+
import CLILog from "../../lib/CLILog.js";
|
|
3
|
+
|
|
4
|
+
const uncachedImport = async (path = '', options = {}) => {
|
|
5
|
+
const modulePath = `${path}?update=${Date.now()}`
|
|
6
|
+
const contents = await import(modulePath);
|
|
7
|
+
return (contents?.default && options?.default) ? contents.default : contents;
|
|
8
|
+
};
|
|
3
9
|
|
|
4
10
|
export default async (path = '', options = {}) => {
|
|
5
11
|
const sanitizedPath = path?.charAt(0) === '/' ? path.substring(1, path.length) : path;
|
|
6
|
-
|
|
12
|
+
// NOTE: Use timestamp to cache bust on import() below.
|
|
13
|
+
const buildPath = `${process.cwd()}/.joystick/build/${sanitizedPath}`;
|
|
7
14
|
const pathExists = fs.existsSync(buildPath);
|
|
8
15
|
|
|
9
16
|
if (!pathExists) {
|
|
10
|
-
|
|
17
|
+
CLILog(`[test.load] Path at ${buildPath} not found.`, {
|
|
11
18
|
level: 'warning',
|
|
12
19
|
docs: 'https://cheatcode.co/docs/joystick/test/load',
|
|
13
20
|
});
|
|
@@ -15,7 +22,5 @@ export default async (path = '', options = {}) => {
|
|
|
15
22
|
return null;
|
|
16
23
|
}
|
|
17
24
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
return (contents?.default && options?.default) ? contents.default : contents;
|
|
25
|
+
return uncachedImport(buildPath, options);
|
|
21
26
|
};
|
|
@@ -1 +1,9 @@
|
|
|
1
|
-
export default {
|
|
1
|
+
export default {
|
|
2
|
+
job: (queueName = '', job = {}) => {
|
|
3
|
+
// TODO: Add a job of name to queueName.
|
|
4
|
+
// TODO: Run the job immediately.
|
|
5
|
+
// TODO: Return result of job.
|
|
6
|
+
// TODO: In test, either verify that response is as expected, or, test for known
|
|
7
|
+
// side-effects of the job in database.
|
|
8
|
+
},
|
|
9
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export default (eventType = '', eventTarget = '', dom = {}) => {
|
|
2
|
+
const app = dom?.document?.querySelector('#app');
|
|
3
|
+
|
|
4
|
+
const target = app?.querySelector(eventTarget);
|
|
5
|
+
const eventToDispatch = new Event(eventType);
|
|
6
|
+
|
|
7
|
+
target.dispatchEvent(eventToDispatch);
|
|
8
|
+
|
|
9
|
+
return true;
|
|
10
|
+
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
|
|
2
|
+
import { parseHTML } from 'linkedom';
|
|
3
|
+
import joystick from '@joystick.js/ui-canary';
|
|
4
|
+
import fetch from 'node-fetch';
|
|
5
|
+
import { URL, URLSearchParams } from 'url';
|
|
6
|
+
import event from './event.js';
|
|
7
|
+
import load from "../load/index.js";
|
|
8
|
+
|
|
9
|
+
const bootstrapWindow = async (pathToComponent = '') => {
|
|
10
|
+
const url = new URL(`${window?.location?.origin}/api/_test/bootstrap`);
|
|
11
|
+
url.search = new URLSearchParams({ pathToComponent });
|
|
12
|
+
|
|
13
|
+
const bootstrap = await fetch(url).then(async (response) => response.json());
|
|
14
|
+
|
|
15
|
+
window.joystick = {};
|
|
16
|
+
window.joystick.settings = {}; // TODO
|
|
17
|
+
window.__joystick_data__ = bootstrap?.data || {};
|
|
18
|
+
window.__joystick_i18n__ = bootstrap?.translations || {};
|
|
19
|
+
window.__joystick_req__ = bootstrap?.req; // TODO
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const loadDOM = () => {
|
|
23
|
+
const dom = parseHTML(`
|
|
24
|
+
<html>
|
|
25
|
+
<head></head>
|
|
26
|
+
<body>
|
|
27
|
+
<div id="app"></div>
|
|
28
|
+
<meta name="csrf" content="joystick_test" />
|
|
29
|
+
</body>
|
|
30
|
+
</html>
|
|
31
|
+
`);
|
|
32
|
+
const { window, document, Element, Event, HTMLElement } = dom;
|
|
33
|
+
|
|
34
|
+
global.window = window;
|
|
35
|
+
global.document = document;
|
|
36
|
+
global.HTMLElement = HTMLElement;
|
|
37
|
+
global.Element = Element;
|
|
38
|
+
global.Event = Event;
|
|
39
|
+
global.console = {
|
|
40
|
+
log: console.log,
|
|
41
|
+
warn: console.warn,
|
|
42
|
+
error: console.error,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
return dom;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export default async (pathToComponent = '', options = {}) => {
|
|
49
|
+
const dom = loadDOM();
|
|
50
|
+
|
|
51
|
+
window.fetch = fetch;
|
|
52
|
+
window.location = {
|
|
53
|
+
origin: `http://localhost:${process.env.PORT}`,
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
await bootstrapWindow(pathToComponent);
|
|
57
|
+
|
|
58
|
+
// NOTE: Force default to true as that's the prescribed pattern for
|
|
59
|
+
// Joystick component files.
|
|
60
|
+
const Component = await load(pathToComponent, { default: true });
|
|
61
|
+
const component = joystick.mount(Component, options?.props || {}, dom?.document.querySelector('#app'));
|
|
62
|
+
|
|
63
|
+
// NOTE: Used internally in @joystick.js/ui to control component behavior.
|
|
64
|
+
component.isTest = true;
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
dom,
|
|
68
|
+
instance: component,
|
|
69
|
+
test: {
|
|
70
|
+
data: async (input = {}) => {
|
|
71
|
+
return component?.data?.refetch(input);
|
|
72
|
+
},
|
|
73
|
+
renderToHTML: () => {
|
|
74
|
+
const html = component?.renderToHTML();
|
|
75
|
+
const whenRegex = new RegExp('<when>|</when>', 'g');
|
|
76
|
+
return html?.wrapped?.replace(whenRegex, '')?.replace(/\n|\t/g, ' ')?.replace(/> *</g, '><');
|
|
77
|
+
},
|
|
78
|
+
method: (methodName = '', ...methodArgs) => {
|
|
79
|
+
const methodToCall = component?.methods[methodName];
|
|
80
|
+
|
|
81
|
+
if (!methodToCall) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return methodToCall(...methodArgs);
|
|
86
|
+
},
|
|
87
|
+
event: (eventType = '', eventTarget = '') => {
|
|
88
|
+
return event(eventType, eventTarget, dom);
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
};
|
package/src/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import test from './test.js';
|
|
2
2
|
import api from './helpers/api/index.js';
|
|
3
|
-
import
|
|
3
|
+
import render from './helpers/render/index.js';
|
|
4
4
|
import databases from './helpers/databases/index.js';
|
|
5
5
|
import email from './helpers/email/index.js';
|
|
6
6
|
import queues from './helpers/queues/index.js';
|
|
@@ -10,8 +10,17 @@ import uploaders from './helpers/uploaders/index.js';
|
|
|
10
10
|
import websockets from './helpers/websockets/index.js';
|
|
11
11
|
|
|
12
12
|
export default {
|
|
13
|
+
accounts: {
|
|
14
|
+
// login: test.login,
|
|
15
|
+
signup: test.signupUser,
|
|
16
|
+
delete: test.deleteUser,
|
|
17
|
+
},
|
|
18
|
+
after: test.after,
|
|
19
|
+
afterEach: test.afterEach,
|
|
13
20
|
api,
|
|
14
|
-
|
|
21
|
+
before: test.before,
|
|
22
|
+
beforeEach: test.beforeEach,
|
|
23
|
+
render,
|
|
15
24
|
databases,
|
|
16
25
|
email,
|
|
17
26
|
load,
|
|
@@ -18,7 +18,7 @@ export default (message = '', options = {}) => {
|
|
|
18
18
|
|
|
19
19
|
const color = options.level ? colors[options.level] : 'gray';
|
|
20
20
|
const title = options.level ? titles[options.level] : 'Log';
|
|
21
|
-
const docs = options.docs || 'https://
|
|
21
|
+
const docs = options.docs || 'https://github.com/cheatcode/joystick';
|
|
22
22
|
|
|
23
23
|
console.log(`\n${(options.padding || '')}${rainbowRoad()}\n`);
|
|
24
24
|
console.log(`${(options.padding || '')}${chalk[color](`${title}:`)}\n`)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
export default (length = 16) => {
|
|
2
|
+
let result = [];
|
|
3
|
+
let character = [
|
|
4
|
+
"0",
|
|
5
|
+
"1",
|
|
6
|
+
"2",
|
|
7
|
+
"3",
|
|
8
|
+
"4",
|
|
9
|
+
"5",
|
|
10
|
+
"6",
|
|
11
|
+
"7",
|
|
12
|
+
"8",
|
|
13
|
+
"9",
|
|
14
|
+
"a",
|
|
15
|
+
"b",
|
|
16
|
+
"c",
|
|
17
|
+
"d",
|
|
18
|
+
"e",
|
|
19
|
+
"f",
|
|
20
|
+
"g",
|
|
21
|
+
"h",
|
|
22
|
+
"i",
|
|
23
|
+
"j",
|
|
24
|
+
"k",
|
|
25
|
+
"l",
|
|
26
|
+
"m",
|
|
27
|
+
"n",
|
|
28
|
+
"o",
|
|
29
|
+
"p",
|
|
30
|
+
"q",
|
|
31
|
+
"r",
|
|
32
|
+
"s",
|
|
33
|
+
"t",
|
|
34
|
+
"u",
|
|
35
|
+
"v",
|
|
36
|
+
"w",
|
|
37
|
+
"x",
|
|
38
|
+
"y",
|
|
39
|
+
"z",
|
|
40
|
+
"A",
|
|
41
|
+
"B",
|
|
42
|
+
"C",
|
|
43
|
+
"D",
|
|
44
|
+
"E",
|
|
45
|
+
"F",
|
|
46
|
+
"G",
|
|
47
|
+
"H",
|
|
48
|
+
"I",
|
|
49
|
+
"J",
|
|
50
|
+
"K",
|
|
51
|
+
"L",
|
|
52
|
+
"M",
|
|
53
|
+
"N",
|
|
54
|
+
"O",
|
|
55
|
+
"P",
|
|
56
|
+
"Q",
|
|
57
|
+
"R",
|
|
58
|
+
"S",
|
|
59
|
+
"T",
|
|
60
|
+
"U",
|
|
61
|
+
"V",
|
|
62
|
+
"W",
|
|
63
|
+
"X",
|
|
64
|
+
"Y",
|
|
65
|
+
"Z",
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
for (let n = 0; n < length; n++) {
|
|
69
|
+
result.push(character[Math.floor(Math.random() * 16)]);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return result.join("");
|
|
73
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/* eslint-disable consistent-return */
|
|
2
|
+
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import CLILog from "./CLILog.js";
|
|
5
|
+
import isValidJSONString from "./isValidJSONString.js";
|
|
6
|
+
|
|
7
|
+
const warnIfInvalidJSONInSettings = (settings = '') => {
|
|
8
|
+
try {
|
|
9
|
+
const isValidJSON = isValidJSONString(settings);
|
|
10
|
+
const context = process.env.NODE_ENV === 'test' ? 'test' : 'start';
|
|
11
|
+
|
|
12
|
+
if (!isValidJSON) {
|
|
13
|
+
CLILog(
|
|
14
|
+
`Failed to parse settings file. Double-check the syntax in your settings.${process.env.NODE_ENV}.json file at the root of your project and rerun joystick ${context}.`,
|
|
15
|
+
{
|
|
16
|
+
level: "danger",
|
|
17
|
+
docs: `https://cheatcode.co/docs/joystick/environment-settings`,
|
|
18
|
+
tools: [{ title: "JSON Linter", url: "https://jsonlint.com/" }],
|
|
19
|
+
}
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
process.exit(0);
|
|
23
|
+
}
|
|
24
|
+
} catch (exception) {
|
|
25
|
+
throw new Error(`[loadSettings.warnIfInvalidJSONInSettings] ${exception.message}`);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const getSettings = (settingsPath = '') => {
|
|
30
|
+
try {
|
|
31
|
+
return fs.readFileSync(settingsPath, 'utf-8');
|
|
32
|
+
} catch (exception) {
|
|
33
|
+
throw new Error(`[loadSettings.getSettings] ${exception.message}`);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const warnIfSettingsNotFound = (settingsPath = '') => {
|
|
38
|
+
try {
|
|
39
|
+
const hasSettingsFile = fs.existsSync(settingsPath);
|
|
40
|
+
const context = process.env.NODE_ENV === 'test' ? 'test' : 'start';
|
|
41
|
+
|
|
42
|
+
if (!hasSettingsFile) {
|
|
43
|
+
CLILog(
|
|
44
|
+
`A settings file could not be found for this environment (${process.env.NODE_ENV}). Create a settings.${process.env.NODE_ENV}.json file at the root of your project and rerun joystick ${context}.`,
|
|
45
|
+
{
|
|
46
|
+
level: "danger",
|
|
47
|
+
docs: `https://cheatcode.co/docs/joystick/cli/${context}`,
|
|
48
|
+
}
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
process.exit(0);
|
|
52
|
+
}
|
|
53
|
+
} catch (exception) {
|
|
54
|
+
throw new Error(`[loadSettings.warnIfSettingsNotFound] ${exception.message}`);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const validateOptions = (options) => {
|
|
59
|
+
try {
|
|
60
|
+
if (!options) throw new Error('options object is required.');
|
|
61
|
+
if (!options.environment) throw new Error('options.environment is required.');
|
|
62
|
+
} catch (exception) {
|
|
63
|
+
throw new Error(`[loadSettings.validateOptions] ${exception.message}`);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const loadSettings = (options, { resolve, reject }) => {
|
|
68
|
+
try {
|
|
69
|
+
validateOptions(options);
|
|
70
|
+
|
|
71
|
+
const settingsPath = `${process.cwd()}/settings.${options.environment}.json`;
|
|
72
|
+
warnIfSettingsNotFound(settingsPath);
|
|
73
|
+
const settings = getSettings(settingsPath);
|
|
74
|
+
warnIfInvalidJSONInSettings(settings);
|
|
75
|
+
|
|
76
|
+
process.env.JOYSTICK_SETTINGS = settings;
|
|
77
|
+
|
|
78
|
+
resolve({
|
|
79
|
+
parsed: JSON.parse(settings),
|
|
80
|
+
unparsed: settings,
|
|
81
|
+
});
|
|
82
|
+
} catch (exception) {
|
|
83
|
+
reject(`[loadSettings] ${exception.message}`);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export default (options) =>
|
|
88
|
+
new Promise((resolve, reject) => {
|
|
89
|
+
loadSettings(options, { resolve, reject });
|
|
90
|
+
});
|
package/src/test.js
CHANGED
|
@@ -1,13 +1,78 @@
|
|
|
1
1
|
import test from 'ava';
|
|
2
|
+
import fetch from 'node-fetch';
|
|
3
|
+
|
|
4
|
+
let users = [];
|
|
2
5
|
|
|
3
6
|
class Test {
|
|
4
7
|
constructor() {
|
|
5
|
-
|
|
6
8
|
}
|
|
7
|
-
|
|
9
|
+
|
|
10
|
+
async signupUser(usersToSignup = []) {
|
|
11
|
+
for (let i = 0; i < usersToSignup?.length; i += 1) {
|
|
12
|
+
const userToSignup = usersToSignup[i];
|
|
13
|
+
const user = await fetch(`http://localhost:${process.env.PORT}/api/_test/accounts/signup`, {
|
|
14
|
+
method: 'POST',
|
|
15
|
+
mode: "cors",
|
|
16
|
+
headers: {
|
|
17
|
+
"Content-Type": "application/json",
|
|
18
|
+
},
|
|
19
|
+
body: JSON.stringify(userToSignup)
|
|
20
|
+
}).then((response) => response.json());
|
|
21
|
+
|
|
22
|
+
users.push(user);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
deleteUser(userId = '') {
|
|
27
|
+
return fetch(`http://localhost:${process.env.PORT}/api/_test/accounts`, {
|
|
28
|
+
method: 'DELETE',
|
|
29
|
+
mode: "cors",
|
|
30
|
+
headers: {
|
|
31
|
+
"Content-Type": "application/json",
|
|
32
|
+
},
|
|
33
|
+
body: JSON.stringify({ userId })
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
before(callback = null) {
|
|
38
|
+
// NOTE: Prefer serial before to async to align better with
|
|
39
|
+
// expectations and avoid confusion.
|
|
40
|
+
return test.serial.before(callback);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
beforeEach(callback = null) {
|
|
44
|
+
return test.beforeEach(callback);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
after(callback = null) {
|
|
48
|
+
// NOTE: Prefer after always to guarantee cleanup and avoid
|
|
49
|
+
// messy test suites that may or may not cleanup due to failures.
|
|
50
|
+
return test.after.always(callback);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
afterEach(callback = null) {
|
|
54
|
+
// NOTE: Prefer afterEach always to guarantee cleanup and avoid
|
|
55
|
+
// messy test suites that may or may not cleanup due to failures.
|
|
56
|
+
return test.afterEach.always(callback);
|
|
57
|
+
}
|
|
58
|
+
|
|
8
59
|
that(description = '', callback = null) {
|
|
9
|
-
|
|
60
|
+
// NOTE: Always run serial so we don't have collisions on component instances
|
|
61
|
+
// for DOM tests.
|
|
62
|
+
return test.serial(description, callback);
|
|
10
63
|
}
|
|
11
64
|
}
|
|
12
65
|
|
|
13
|
-
|
|
66
|
+
const testInstance = new Test();
|
|
67
|
+
|
|
68
|
+
test.serial.after.always(async () => {
|
|
69
|
+
// NOTE: Nuke users who were created as part of the test run to avoid
|
|
70
|
+
// data collisions in other tests/suites.
|
|
71
|
+
|
|
72
|
+
for (let i = 0; i < users?.length; i += 1) {
|
|
73
|
+
const user = users[i];
|
|
74
|
+
await testInstance.deleteUser(user?._id || user?.user_id);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
export default testInstance;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
var e={};export{e as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export default {};
|