Migrate to TypeScript partially
This commit is contained in:
parent
3b033be048
commit
5d80b8e06a
|
|
@ -5,6 +5,7 @@ module.exports = api => ({
|
|||
useBuiltIns: false,
|
||||
loose: true,
|
||||
}],
|
||||
'@babel/preset-typescript',
|
||||
],
|
||||
plugins: [
|
||||
'@babel/plugin-syntax-dynamic-import',
|
||||
|
|
@ -19,6 +20,7 @@ module.exports = api => ({
|
|||
['@babel/preset-env', {
|
||||
targets: { esmodules: true },
|
||||
}],
|
||||
'@babel/preset-typescript',
|
||||
],
|
||||
plugins: [
|
||||
'@babel/plugin-syntax-dynamic-import',
|
||||
|
|
@ -29,6 +31,7 @@ module.exports = api => ({
|
|||
['@babel/preset-env', {
|
||||
targets: { node: 'current' },
|
||||
}],
|
||||
'@babel/preset-typescript',
|
||||
],
|
||||
plugins: [
|
||||
'babel-plugin-dynamic-import-node',
|
||||
|
|
|
|||
35
package.json
35
package.json
|
|
@ -12,8 +12,8 @@
|
|||
"scripts": {
|
||||
"dev": "webpack-dev-server",
|
||||
"build": "rimraf public/app && webpack --mode=production",
|
||||
"lint": "eslint --ext=.js,.vue -f=beauty .",
|
||||
"test": "jest",
|
||||
"lint": "eslint --ext=.js,.vue,.ts -f=beauty .",
|
||||
"test": "tsc -p . && jest",
|
||||
"codecov": "codecov -F js"
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
@ -36,8 +36,13 @@
|
|||
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
|
||||
"@babel/plugin-transform-runtime": "^7.3.4",
|
||||
"@babel/preset-env": "^7.3.4",
|
||||
"@babel/preset-typescript": "^7.3.3",
|
||||
"@gplane/tsconfig": "^1.0.0",
|
||||
"@types/jest": "^24.0.11",
|
||||
"@types/jquery": "^3.3.9",
|
||||
"@types/toastr": "^2.1.36",
|
||||
"@typescript-eslint/eslint-plugin": "^1.4.2",
|
||||
"@typescript-eslint/parser": "^1.4.2",
|
||||
"@vue/test-utils": "^1.0.0-beta.29",
|
||||
"autoprefixer": "^9.5.0",
|
||||
"babel-eslint": "^10.0.1",
|
||||
|
|
@ -65,6 +70,7 @@
|
|||
"style-loader": "^0.23.1",
|
||||
"stylus": "^0.54.5",
|
||||
"stylus-loader": "^3.0.2",
|
||||
"typescript": "^3.3.3333",
|
||||
"url-loader": "^1.1.2",
|
||||
"vue-jest": "^4.0.0-beta.2",
|
||||
"vue-loader": "^15.7.0",
|
||||
|
|
@ -130,18 +136,37 @@
|
|||
"prefer-object-spread": 0,
|
||||
"import/no-unresolved": 0,
|
||||
"project/linebreak-between-tests": 2
|
||||
}
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"*.ts"
|
||||
],
|
||||
"parserOptions": {
|
||||
"parser": "@typescript-eslint/parser"
|
||||
},
|
||||
"plugins": [
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"rules": {
|
||||
"no-undef": 0,
|
||||
"no-unused-vars": 0,
|
||||
"no-invalid-this": 0
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"jest": {
|
||||
"resetMocks": true,
|
||||
"timers": "fake",
|
||||
"transform": {
|
||||
"^.+\\.js$": "babel-jest",
|
||||
".*\\.(t|j)s$": "babel-jest",
|
||||
".*\\.vue$": "vue-jest"
|
||||
},
|
||||
"moduleFileExtensions": [
|
||||
"js",
|
||||
"vue",
|
||||
"ts",
|
||||
"json",
|
||||
"node"
|
||||
],
|
||||
|
|
@ -158,6 +183,6 @@
|
|||
"<rootDir>/resources/assets/tests/setup",
|
||||
"<rootDir>/resources/assets/tests/utils"
|
||||
],
|
||||
"testRegex": "resources/assets/tests/.*\\.(spec|test)\\.js$"
|
||||
"testRegex": "resources/assets/tests/.*\\.(spec|test)\\.(t|j)s$"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import { init } from './net'
|
||||
|
||||
export async function checkForUpdates() {
|
||||
export async function checkForUpdates(): Promise<void> {
|
||||
const response = await fetch(`${blessing.base_url}/admin/update/check`, init)
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json()
|
||||
if (data.available) {
|
||||
document.querySelector(`[href="${blessing.base_url}/admin/update"]`)
|
||||
.innerHTML += `
|
||||
const el = document.querySelector(`[href="${blessing.base_url}/admin/update"]`)
|
||||
if (data.available && el) {
|
||||
el.innerHTML += `
|
||||
<span class="pull-right-container">
|
||||
<span class="label label-primary pull-right">v${data.latest}</span>
|
||||
</span>`
|
||||
|
|
@ -15,14 +15,14 @@ export async function checkForUpdates() {
|
|||
}
|
||||
}
|
||||
|
||||
export async function checkForPluginUpdates() {
|
||||
export async function checkForPluginUpdates(): Promise<void> {
|
||||
const response = await fetch(`${blessing.base_url}/admin/plugins/market/check`, init)
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json()
|
||||
if (data.available) {
|
||||
document.querySelector(`[href="${blessing.base_url}/admin/plugins/market"]`)
|
||||
.innerHTML += `
|
||||
const el = document.querySelector(`[href="${blessing.base_url}/admin/plugins/market"]`)
|
||||
if (data.available && el) {
|
||||
el.innerHTML += `
|
||||
<span class="pull-right-container">
|
||||
<span class="label label-success pull-right">${data.plugins.length}</span>
|
||||
</span>`
|
||||
|
|
@ -30,5 +30,7 @@ export async function checkForPluginUpdates() {
|
|||
}
|
||||
}
|
||||
|
||||
window.checkForUpdates = checkForUpdates
|
||||
window.checkForPluginUpdates = checkForPluginUpdates
|
||||
Object.assign(window, {
|
||||
checkForUpdates,
|
||||
checkForPluginUpdates,
|
||||
})
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
/** @type {{ [name: string]: Function[] }} */
|
||||
const bus = Object.create(null)
|
||||
|
||||
/**
|
||||
* @param {string} eventName
|
||||
* @param {Function} listener
|
||||
*/
|
||||
export function on(eventName, listener) {
|
||||
(bus[eventName] || (bus[eventName] = [])).push(listener)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} eventName
|
||||
* @param {any} payload
|
||||
*/
|
||||
export function emit(eventName, payload) {
|
||||
bus[eventName] && bus[eventName].forEach(listener => listener(payload))
|
||||
}
|
||||
|
||||
blessing.event = { on, emit }
|
||||
11
resources/assets/src/js/event.ts
Normal file
11
resources/assets/src/js/event.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
const bus: { [name: string]: CallableFunction[] } = Object.create(null)
|
||||
|
||||
export function on(eventName: string, listener: CallableFunction) {
|
||||
(bus[eventName] || (bus[eventName] = [])).push(listener)
|
||||
}
|
||||
|
||||
export function emit(eventName: string, payload?: any) {
|
||||
bus[eventName] && bus[eventName].forEach(listener => listener(payload))
|
||||
}
|
||||
|
||||
blessing.event = { on, emit }
|
||||
|
|
@ -1,27 +1,26 @@
|
|||
import Vue from 'vue'
|
||||
|
||||
/**
|
||||
* Translate according to given key.
|
||||
*
|
||||
* @param {string} key
|
||||
* @param {object} parameters
|
||||
* @return {string}
|
||||
*/
|
||||
export function trans(key, parameters = Object.create(null)) {
|
||||
export function trans(key: string, parameters = Object.create(null)): string {
|
||||
const segments = key.split('.')
|
||||
let temp = blessing.i18n || Object.create(null)
|
||||
let temp = (blessing.i18n || Object.create(null)) as { [k: string]: string | { [k: string]: string } }
|
||||
let result = ''
|
||||
|
||||
for (const segment of segments) {
|
||||
if (!temp[segment]) {
|
||||
return key
|
||||
}
|
||||
temp = temp[segment]
|
||||
const middle = temp[segment]
|
||||
if (typeof middle === 'string') {
|
||||
result = middle
|
||||
} else {
|
||||
temp = middle
|
||||
}
|
||||
}
|
||||
|
||||
Object.keys(parameters)
|
||||
.forEach(slot => (temp = temp.replace(`:${slot}`, parameters[slot])))
|
||||
.forEach(slot => (result = result.replace(`:${slot}`, parameters[slot])))
|
||||
|
||||
return temp
|
||||
return result
|
||||
}
|
||||
|
||||
Vue.use(_Vue => {
|
||||
|
|
@ -41,4 +40,5 @@ Vue.use(_Vue => {
|
|||
}
|
||||
})
|
||||
})
|
||||
|
||||
window.trans = trans
|
||||
|
|
@ -15,7 +15,7 @@ export async function logout() {
|
|||
}
|
||||
|
||||
const { msg } = await post('/auth/logout')
|
||||
setTimeout(() => (window.location = blessing.base_url), 1000)
|
||||
setTimeout(() => (window.location.href = blessing.base_url), 1000)
|
||||
swal({ type: 'success', text: msg })
|
||||
}
|
||||
|
||||
|
|
@ -4,30 +4,28 @@ import { queryStringify } from './utils'
|
|||
import { showAjaxError } from './notify'
|
||||
|
||||
class HTTPError extends Error {
|
||||
constructor(message, response) {
|
||||
response: Response
|
||||
|
||||
constructor(message: string, response: Response) {
|
||||
super(message)
|
||||
this.response = response
|
||||
}
|
||||
}
|
||||
|
||||
const empty = Object.create(null)
|
||||
/** @type Request */
|
||||
export const init = {
|
||||
export const init: RequestInit = {
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
headers: new Headers({
|
||||
Accept: 'application/json',
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
||||
function retrieveToken() {
|
||||
const csrfField = document.querySelector('meta[name="csrf-token"]')
|
||||
return csrfField && csrfField.content
|
||||
const csrfField: HTMLMetaElement | null = document.querySelector('meta[name="csrf-token"]')
|
||||
return (csrfField && csrfField.content) || ''
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Request} request
|
||||
*/
|
||||
export async function walkFetch(request) {
|
||||
export async function walkFetch(request: Request): Promise<any> {
|
||||
request.headers.set('X-CSRF-TOKEN', retrieveToken())
|
||||
|
||||
try {
|
||||
|
|
@ -45,7 +43,7 @@ export async function walkFetch(request) {
|
|||
}
|
||||
}
|
||||
|
||||
export function get(url, params = empty) {
|
||||
export function get(url: string, params = empty): Promise<any> {
|
||||
emit('beforeFetch', {
|
||||
method: 'GET',
|
||||
url,
|
||||
|
|
@ -57,7 +55,7 @@ export function get(url, params = empty) {
|
|||
return walkFetch(new Request(`${blessing.base_url}${url}${qs && `?${qs}`}`, init))
|
||||
}
|
||||
|
||||
export function post(url, data = empty) {
|
||||
export function post(url: string, data = empty): Promise<any> {
|
||||
emit('beforeFetch', {
|
||||
method: 'POST',
|
||||
url,
|
||||
|
|
@ -77,10 +75,9 @@ export function post(url, data = empty) {
|
|||
}
|
||||
|
||||
Vue.use(_Vue => {
|
||||
_Vue.prototype.$http = {
|
||||
get,
|
||||
post,
|
||||
}
|
||||
Object.defineProperty(_Vue.prototype, '$http', {
|
||||
get: () => ({ get, post }),
|
||||
})
|
||||
})
|
||||
|
||||
blessing.fetch = { get, post }
|
||||
|
|
@ -2,31 +2,23 @@
|
|||
import $ from 'jquery'
|
||||
import Swal from 'sweetalert2'
|
||||
import toastr from 'toastr'
|
||||
import { ModalOptions } from '../shims'
|
||||
import { trans } from './i18n'
|
||||
|
||||
/**
|
||||
* Show modal if error occured when sending an ajax request.
|
||||
*
|
||||
* @param {Error} error
|
||||
* @return {void}
|
||||
*/
|
||||
export function showAjaxError(error) {
|
||||
export function showAjaxError(error: Error): void {
|
||||
showModal(error.message.replace(/\n/g, '<br>'), trans('general.fatalError'), 'danger')
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a bootstrap modal.
|
||||
*
|
||||
* @param {string} msg Modal content
|
||||
* @param {string} title Modal title
|
||||
* @param {string} type Modal type, default|info|success|warning|error
|
||||
* @param {object} options All $.fn.modal options, plus { btnText, callback, destroyOnClose }
|
||||
* @return {void}
|
||||
*/
|
||||
export function showModal(msg, title = 'Message', type = 'default', options = {}) {
|
||||
export function showModal(
|
||||
msg: string, title = 'Message',
|
||||
type = 'default',
|
||||
options: ModalOptions = {}
|
||||
): void {
|
||||
const btnType = type === 'default' ? 'btn-primary' : 'btn-outline'
|
||||
const btnText = options.btnText || 'OK'
|
||||
const onClick = options.callback === undefined ? 'data-dismiss="modal"' : `onclick="${options.callback}"`
|
||||
const onClick = options.callback === undefined
|
||||
? 'data-dismiss="modal"'
|
||||
: `onclick="${options.callback}"`
|
||||
const destroyOnClose = options.destroyOnClose !== false
|
||||
|
||||
const dom = `
|
||||
|
|
@ -51,9 +43,9 @@ export function showModal(msg, title = 'Message', type = 'default', options = {}
|
|||
|
||||
$(dom)
|
||||
.on('hidden.bs.modal', /* istanbul ignore next */ function modal() {
|
||||
// eslint-disable-next-line no-invalid-this
|
||||
destroyOnClose && $(this).remove()
|
||||
})
|
||||
// @ts-ignore
|
||||
.modal(options)
|
||||
}
|
||||
|
||||
|
|
@ -62,13 +54,9 @@ const swalInstance = Swal.mixin({
|
|||
cancelButtonText: trans('general.cancel'),
|
||||
})
|
||||
|
||||
/**
|
||||
* @param {import('sweetalert2').SweetAlertOptions} options
|
||||
*/
|
||||
export function swal(options) {
|
||||
export function swal(options: import('sweetalert2').SweetAlertOptions) {
|
||||
return swalInstance.fire(options)
|
||||
}
|
||||
|
||||
window.toastr = toastr
|
||||
window.swal = swal
|
||||
blessing.notify = { showModal }
|
||||
Object.assign(window, { toastr, swal })
|
||||
Object.assign(blessing, { notify: { showModal } })
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
/**
|
||||
* @param {Function} func
|
||||
* @param {number} delay
|
||||
*/
|
||||
export function debounce(func, delay) {
|
||||
let timer
|
||||
return () => {
|
||||
clearTimeout(timer)
|
||||
timer = setTimeout(func, delay)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get parameters in query string with key.
|
||||
*
|
||||
* @param {string} key
|
||||
* @param {string} defaultValue
|
||||
* @return {string}
|
||||
*/
|
||||
export function queryString(key, defaultValue) {
|
||||
const result = location.search.match(new RegExp(`[?&]${key}=([^&]+)`, 'i'))
|
||||
|
||||
if (result === null || result.length < 1) {
|
||||
return defaultValue
|
||||
}
|
||||
return result[1]
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize data to URL query string
|
||||
*
|
||||
* @param {object} data
|
||||
* @returns {string}
|
||||
*/
|
||||
export function queryStringify(params) {
|
||||
return Object
|
||||
.keys(params)
|
||||
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
|
||||
.join('&')
|
||||
}
|
||||
23
resources/assets/src/js/utils.ts
Normal file
23
resources/assets/src/js/utils.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
export function debounce(func: CallableFunction, delay: number) {
|
||||
let timer: number
|
||||
return () => {
|
||||
clearTimeout(timer)
|
||||
timer = setTimeout(func, delay)
|
||||
}
|
||||
}
|
||||
|
||||
export function queryString(key: string, defaultValue: string = ''): string {
|
||||
const result = location.search.match(new RegExp(`[?&]${key}=([^&]+)`, 'i'))
|
||||
|
||||
if (result === null || result.length < 1) {
|
||||
return defaultValue
|
||||
}
|
||||
return result[1]
|
||||
}
|
||||
|
||||
export function queryStringify(params: { [key: string]: string }): string {
|
||||
return Object
|
||||
.keys(params)
|
||||
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
|
||||
.join('&')
|
||||
}
|
||||
10
resources/assets/src/shims.d.ts
vendored
10
resources/assets/src/shims.d.ts
vendored
|
|
@ -1,7 +1,9 @@
|
|||
/* eslint-disable camelcase */
|
||||
import Vue from 'vue'
|
||||
import * as JQuery from 'jquery'
|
||||
|
||||
declare global {
|
||||
var blessing: {
|
||||
let blessing: {
|
||||
base_url: string
|
||||
debug: boolean
|
||||
env: string
|
||||
|
|
@ -48,3 +50,9 @@ declare module 'vue/types/vue' {
|
|||
$route: string[]
|
||||
}
|
||||
}
|
||||
|
||||
interface ModalOptions {
|
||||
btnText?: string,
|
||||
callback?: CallableFunction,
|
||||
destroyOnClose?: boolean
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,3 +4,4 @@ env:
|
|||
rules:
|
||||
no-empty-function: 0
|
||||
func-names: 0
|
||||
no-extra-parens: 0
|
||||
|
|
|
|||
|
|
@ -16,19 +16,17 @@ test('check for BS updates', async () => {
|
|||
json: () => Promise.resolve({ available: true, latest: '4.0.0' }),
|
||||
})
|
||||
|
||||
document.body.innerHTML = `
|
||||
<a href="/admin/update"></a>
|
||||
`
|
||||
document.body.innerHTML = '<a href="/admin/update"></a>'
|
||||
|
||||
await checkForUpdates()
|
||||
expect(window.fetch).toBeCalledWith('/admin/update/check', init)
|
||||
expect(document.querySelector('a').innerHTML).toBe('')
|
||||
expect(document.querySelector('a')!.innerHTML).toBe('')
|
||||
|
||||
await checkForUpdates()
|
||||
expect(document.querySelector('a').innerHTML).toBe('')
|
||||
expect(document.querySelector('a')!.innerHTML).toBe('')
|
||||
|
||||
await checkForUpdates()
|
||||
expect(document.querySelector('a').innerHTML).toContain('4.0.0')
|
||||
expect(document.querySelector('a')!.innerHTML).toContain('4.0.0')
|
||||
})
|
||||
|
||||
test('check for plugins updates', async () => {
|
||||
|
|
@ -43,17 +41,29 @@ test('check for plugins updates', async () => {
|
|||
json: () => Promise.resolve({ available: true, plugins: [{}] }),
|
||||
})
|
||||
|
||||
document.body.innerHTML = `
|
||||
<a href="/admin/plugins/market"></a>
|
||||
`
|
||||
document.body.innerHTML = '<a href="/admin/plugins/market"></a>'
|
||||
|
||||
await checkForPluginUpdates()
|
||||
expect(window.fetch).toBeCalledWith('/admin/plugins/market/check', init)
|
||||
expect(document.querySelector('a').innerHTML).toBe('')
|
||||
expect(document.querySelector('a')!.innerHTML).toBe('')
|
||||
|
||||
await checkForPluginUpdates()
|
||||
expect(document.querySelector('a').innerHTML).toBe('')
|
||||
expect(document.querySelector('a')!.innerHTML).toBe('')
|
||||
|
||||
await checkForPluginUpdates()
|
||||
expect(document.querySelector('a').innerHTML).toContain('1')
|
||||
expect(document.querySelector('a')!.innerHTML).toContain('1')
|
||||
})
|
||||
|
||||
test('do not update anything if element not found', async () => {
|
||||
window.fetch = jest.fn()
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: () => Promise.resolve({ available: true, latest: '4.0.0' }),
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: () => Promise.resolve({ available: true, plugins: [{}] }),
|
||||
})
|
||||
|
||||
await Promise.all([checkForUpdates, checkForPluginUpdates])
|
||||
})
|
||||
|
|
@ -1,9 +1,5 @@
|
|||
import * as emitter from '@/js/event'
|
||||
|
||||
test('mount variable to global', () => {
|
||||
expect(window.bsEmitter).toBeFrozen()
|
||||
})
|
||||
|
||||
test('add listener and emit event', () => {
|
||||
const mockA = jest.fn()
|
||||
const mockB = jest.fn()
|
||||
|
|
@ -1,13 +1,18 @@
|
|||
import Vue from 'vue'
|
||||
import * as net from '@/js/net'
|
||||
import { on } from '@/js/event'
|
||||
import { showAjaxError } from '@/js/notify'
|
||||
|
||||
jest.mock('@/js/notify')
|
||||
|
||||
window.Request = function Request(url, init) {
|
||||
this.url = url
|
||||
Object.keys(init).forEach(key => (this[key] = init[key]))
|
||||
this.headers = new Map(Object.entries(init.headers))
|
||||
;(window as Window & { Request: any }).Request = class {
|
||||
headers: Map<string, string>
|
||||
|
||||
constructor(public url: string, init: Request) {
|
||||
this.url = url
|
||||
Object.assign(this, init)
|
||||
this.headers = new Map(Object.entries(init.headers))
|
||||
}
|
||||
}
|
||||
|
||||
test('the GET method', async () => {
|
||||
|
|
@ -96,23 +101,28 @@ test('low level fetch', async () => {
|
|||
text: () => Promise.resolve('text'),
|
||||
})
|
||||
|
||||
const request = { headers: new Map() }
|
||||
const request: RequestInit = { headers: new Headers() }
|
||||
|
||||
const stub = jest.fn()
|
||||
on('fetchError', stub)
|
||||
|
||||
await net.walkFetch(request)
|
||||
await net.walkFetch(request as Request)
|
||||
expect(showAjaxError.mock.calls[0][0]).toBeInstanceOf(Error)
|
||||
expect(showAjaxError.mock.calls[0][0]).toHaveProperty('message', 'network')
|
||||
expect(stub).toBeCalledWith(expect.any(Error))
|
||||
|
||||
await net.walkFetch(request)
|
||||
await net.walkFetch(request as Request)
|
||||
expect(showAjaxError.mock.calls[1][0]).toBeInstanceOf(Error)
|
||||
expect(stub.mock.calls[1][0]).toHaveProperty('message', '404')
|
||||
expect(stub.mock.calls[1][0]).toHaveProperty('response')
|
||||
|
||||
await net.walkFetch(request)
|
||||
await net.walkFetch(request as Request)
|
||||
expect(json).toBeCalled()
|
||||
|
||||
expect(await net.walkFetch(request)).toBe('text')
|
||||
expect(await net.walkFetch(request as Request)).toBe('text')
|
||||
})
|
||||
|
||||
test('inject to Vue instance', () => {
|
||||
expect(typeof Vue.prototype.$http.get).toBe('function')
|
||||
expect(typeof Vue.prototype.$http.post).toBe('function')
|
||||
})
|
||||
|
|
@ -10,6 +10,7 @@ jest.mock('sweetalert2', () => ({
|
|||
}))
|
||||
|
||||
test('show AJAX error', () => {
|
||||
// @ts-ignore
|
||||
$.fn.modal = function () {
|
||||
document.body.innerHTML = this.html()
|
||||
}
|
||||
15
resources/assets/tests/js/types.d.ts
vendored
Normal file
15
resources/assets/tests/js/types.d.ts
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
class Request {
|
||||
url: string
|
||||
|
||||
headers: Map<string, string>
|
||||
}
|
||||
|
||||
interface Window {
|
||||
trans(key: string, parameters: object): string
|
||||
|
||||
blessing: {
|
||||
i18n: object
|
||||
}
|
||||
|
||||
fetch: jest.Mock
|
||||
}
|
||||
|
|
@ -14,7 +14,7 @@ test('debounce', () => {
|
|||
test('queryString', () => {
|
||||
history.pushState({}, 'page', `${location.href}?key=value`)
|
||||
expect(utils.queryString('key')).toBe('value')
|
||||
expect(utils.queryString('a')).toBeUndefined()
|
||||
expect(utils.queryString('a')).toBe('')
|
||||
expect(utils.queryString('a', 'b')).toBe('b')
|
||||
})
|
||||
|
||||
|
|
@ -8,6 +8,12 @@ window.blessing = {
|
|||
version: '4.0.0',
|
||||
}
|
||||
|
||||
window.Headers = class extends Map {
|
||||
constructor(headers = {}) {
|
||||
super(Object.entries(headers))
|
||||
}
|
||||
}
|
||||
|
||||
const noop = () => undefined
|
||||
// eslint-disable-next-line no-console
|
||||
Object.keys(console).forEach(method => (console[method] = noop))
|
||||
|
|
|
|||
18
resources/assets/tests/ts-shims/net.ts
Normal file
18
resources/assets/tests/ts-shims/net.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import * as net from '../../src/js/net'
|
||||
|
||||
export const init = {} as typeof net.init
|
||||
|
||||
export const walkFetch = {} as jest.Mock<
|
||||
ReturnType<typeof net.walkFetch>,
|
||||
Parameters<typeof net.walkFetch>
|
||||
>
|
||||
|
||||
export const get = {} as jest.Mock<
|
||||
ReturnType<typeof net.get>,
|
||||
Parameters<typeof net.get>
|
||||
>
|
||||
|
||||
export const post = {} as jest.Mock<
|
||||
ReturnType<typeof net.post>,
|
||||
Parameters<typeof net.post>
|
||||
>
|
||||
16
resources/assets/tests/ts-shims/notify.ts
Normal file
16
resources/assets/tests/ts-shims/notify.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import * as notify from '../../src/js/notify'
|
||||
|
||||
export const showAjaxError = {} as jest.Mock<
|
||||
ReturnType<typeof notify.showAjaxError>,
|
||||
Parameters<typeof notify.showAjaxError>
|
||||
>
|
||||
|
||||
export const showModal = {} as jest.Mock<
|
||||
ReturnType<typeof notify.showModal>,
|
||||
Parameters<typeof notify.showModal>
|
||||
>
|
||||
|
||||
export const swal = {} as jest.Mock<
|
||||
ReturnType<typeof notify.swal>,
|
||||
Parameters<typeof notify.swal>
|
||||
>
|
||||
22
tsconfig.json
Normal file
22
tsconfig.json
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"extends": "@gplane/tsconfig",
|
||||
"compilerOptions": {
|
||||
"declaration": false,
|
||||
"allowJs": true,
|
||||
"target": "es2015",
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"noEmit": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/js/*": [
|
||||
"./resources/assets/tests/ts-shims/*",
|
||||
"./resources/assets/src/js/*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"vendor"
|
||||
]
|
||||
}
|
||||
|
|
@ -36,7 +36,7 @@ const config = {
|
|||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
test: /\.(t|j)s$/,
|
||||
exclude: /node_modules/,
|
||||
use: ['cache-loader', 'babel-loader'],
|
||||
},
|
||||
|
|
@ -103,8 +103,7 @@ const config = {
|
|||
flatten: true,
|
||||
},
|
||||
{
|
||||
from:
|
||||
'node_modules/admin-lte/dist/css/skins/_all-skins.min.css',
|
||||
from: 'node_modules/admin-lte/dist/css/skins/_all-skins.min.css',
|
||||
to: 'skins',
|
||||
flatten: true,
|
||||
},
|
||||
|
|
@ -114,7 +113,7 @@ const config = {
|
|||
]),
|
||||
],
|
||||
resolve: {
|
||||
extensions: ['.js', '.vue', '.json'],
|
||||
extensions: ['.js', '.ts', '.vue', '.json'],
|
||||
},
|
||||
devtool: devMode ? 'cheap-module-eval-source-map' : false,
|
||||
devServer: {
|
||||
|
|
|
|||
103
yarn.lock
103
yarn.lock
|
|
@ -347,6 +347,13 @@
|
|||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.0.0"
|
||||
|
||||
"@babel/plugin-syntax-typescript@^7.2.0":
|
||||
version "7.3.3"
|
||||
resolved "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.3.3.tgz#a7cc3f66119a9f7ebe2de5383cce193473d65991"
|
||||
integrity sha512-dGwbSMA1YhVS8+31CnPR7LB4pcbrzcV99wQzby4uAfrkZPYZlQ7ImwdpzLqi6Z6IL02b8IAL379CaMwo0x5Lag==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.0.0"
|
||||
|
||||
"@babel/plugin-transform-arrow-functions@^7.2.0":
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz#9aeafbe4d6ffc6563bf8f8372091628f00779550"
|
||||
|
|
@ -548,6 +555,14 @@
|
|||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.0.0"
|
||||
|
||||
"@babel/plugin-transform-typescript@^7.3.2":
|
||||
version "7.3.2"
|
||||
resolved "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.3.2.tgz#59a7227163e55738842f043d9e5bd7c040447d96"
|
||||
integrity sha512-Pvco0x0ZSCnexJnshMfaibQ5hnK8aUHSvjCQhC1JR8eeg+iBwt0AtCO7gWxJ358zZevuf9wPSO5rv+WJcbHPXQ==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.0.0"
|
||||
"@babel/plugin-syntax-typescript" "^7.2.0"
|
||||
|
||||
"@babel/plugin-transform-unicode-regex@^7.2.0":
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.2.0.tgz#4eb8db16f972f8abb5062c161b8b115546ade08b"
|
||||
|
|
@ -605,6 +620,14 @@
|
|||
js-levenshtein "^1.1.3"
|
||||
semver "^5.3.0"
|
||||
|
||||
"@babel/preset-typescript@^7.3.3":
|
||||
version "7.3.3"
|
||||
resolved "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.3.3.tgz#88669911053fa16b2b276ea2ede2ca603b3f307a"
|
||||
integrity sha512-mzMVuIP4lqtn4du2ynEfdO0+RYcslwrZiJHXu4MGaC1ctJiW2fyaeDrtjJGs7R/KebZ1sgowcIoWf4uRpEfKEg==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.0.0"
|
||||
"@babel/plugin-transform-typescript" "^7.3.2"
|
||||
|
||||
"@babel/runtime@^7.3.4":
|
||||
version "7.3.4"
|
||||
resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.3.4.tgz#73d12ba819e365fcf7fd152aed56d6df97d21c83"
|
||||
|
|
@ -736,6 +759,11 @@
|
|||
version "5.7.2"
|
||||
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-5.7.2.tgz#1498c3eb78ee7c78c5488418707de90aaf58d5d7"
|
||||
|
||||
"@gplane/tsconfig@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/@gplane/tsconfig/-/tsconfig-1.0.0.tgz#c7e6d56ece3d045abe46d00aece990d8412440f0"
|
||||
integrity sha512-x0Lxt+oBXGVK8qYYLQvohFYQZlt9KgeVfkGAEXWOq38Gxu5KdcsGzPvyWPMhz5Og/U2pWXbjw4+rN4Qa7CPiWQ==
|
||||
|
||||
"@jest/console@^24.3.0":
|
||||
version "24.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@jest/console/-/console-24.3.0.tgz#7bd920d250988ba0bf1352c4493a48e1cb97671e"
|
||||
|
|
@ -923,6 +951,13 @@
|
|||
dependencies:
|
||||
"@types/jest-diff" "*"
|
||||
|
||||
"@types/jquery@*":
|
||||
version "3.3.29"
|
||||
resolved "https://registry.npmjs.org/@types/jquery/-/jquery-3.3.29.tgz#680a2219ce3c9250483722fccf5570d1e2d08abd"
|
||||
integrity sha512-FhJvBninYD36v3k6c+bVk1DSZwh7B5Dpb/Pyk3HKVsiohn0nhbefZZ+3JXbWQhFyt0MxSl2jRDdGQPHeOHFXrQ==
|
||||
dependencies:
|
||||
"@types/sizzle" "*"
|
||||
|
||||
"@types/jquery@^3.3.9":
|
||||
version "3.3.9"
|
||||
resolved "https://registry.npmjs.org/@types/jquery/-/jquery-3.3.9.tgz#91f2aaf5c1e91fd79598fc00ecb4504d78b51fd7"
|
||||
|
|
@ -932,16 +967,55 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/node/-/node-11.11.3.tgz#7c6b0f8eaf16ae530795de2ad1b85d34bf2f5c58"
|
||||
integrity sha512-wp6IOGu1lxsfnrD+5mX6qwSwWuqsdkKKxTN4aQc4wByHAKZJf9/D4KXPQ1POUjEbnCP5LMggB0OEFNY9OTsMqg==
|
||||
|
||||
"@types/sizzle@*":
|
||||
version "2.3.2"
|
||||
resolved "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.2.tgz#a811b8c18e2babab7d542b3365887ae2e4d9de47"
|
||||
integrity sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg==
|
||||
|
||||
"@types/stack-utils@^1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e"
|
||||
integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==
|
||||
|
||||
"@types/toastr@^2.1.36":
|
||||
version "2.1.36"
|
||||
resolved "https://registry.npmjs.org/@types/toastr/-/toastr-2.1.36.tgz#6b692ec384147324cffe13892e9e5b26bd1e69e7"
|
||||
integrity sha512-mz4eBVCNrH0AyGpt+6DLl/xE+CPrSqMfQs/cMTAGJl8W9W375DNOTYOon1GsuSJqVU1QqZ4jCuARzo4TNQNrDg==
|
||||
dependencies:
|
||||
"@types/jquery" "*"
|
||||
|
||||
"@types/yargs@^12.0.2", "@types/yargs@^12.0.9":
|
||||
version "12.0.9"
|
||||
resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-12.0.9.tgz#693e76a52f61a2f1e7fb48c0eef167b95ea4ffd0"
|
||||
integrity sha512-sCZy4SxP9rN2w30Hlmg5dtdRwgYQfYRiLo9usw8X9cxlf+H4FqM1xX7+sNH7NNKVdbXMJWqva7iyy+fxh/V7fA==
|
||||
|
||||
"@typescript-eslint/eslint-plugin@^1.4.2":
|
||||
version "1.4.2"
|
||||
resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.4.2.tgz#370bc32022d1cc884a5dcf62624ef2024182769d"
|
||||
integrity sha512-6WInypy/cK4rM1dirKbD5p7iFW28DbSRKT/+PGn+DYzBWEvHq5KnZAqQ5cX25JBc0qMkFxJNxNfBbFXJyyzVcw==
|
||||
dependencies:
|
||||
"@typescript-eslint/parser" "1.4.2"
|
||||
"@typescript-eslint/typescript-estree" "1.4.2"
|
||||
requireindex "^1.2.0"
|
||||
tsutils "^3.7.0"
|
||||
|
||||
"@typescript-eslint/parser@1.4.2", "@typescript-eslint/parser@^1.4.2":
|
||||
version "1.4.2"
|
||||
resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-1.4.2.tgz#acfdee2019958a41d308d768e53ded975ef90ce8"
|
||||
integrity sha512-OqLkY9295DXXaWToItUv3olO2//rmzh6Th6Sc7YjFFEpEuennsm5zhygLLvHZjPxPlzrQgE8UDaOPurDylaUuw==
|
||||
dependencies:
|
||||
"@typescript-eslint/typescript-estree" "1.4.2"
|
||||
eslint-scope "^4.0.0"
|
||||
eslint-visitor-keys "^1.0.0"
|
||||
|
||||
"@typescript-eslint/typescript-estree@1.4.2":
|
||||
version "1.4.2"
|
||||
resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-1.4.2.tgz#b16bc36c9a4748a7fca92cba4c2d73c5325c8a85"
|
||||
integrity sha512-wKgi/w6k1v3R4b6oDc20cRWro2gBzp0wn6CAeYC8ExJMfvXMfiaXzw2tT9ilxdONaVWMCk7B9fMdjos7bF/CWw==
|
||||
dependencies:
|
||||
lodash.unescape "4.0.1"
|
||||
semver "5.5.0"
|
||||
|
||||
"@vue/component-compiler-utils@^2.4.0", "@vue/component-compiler-utils@^2.5.1":
|
||||
version "2.5.2"
|
||||
resolved "https://registry.yarnpkg.com/@vue/component-compiler-utils/-/component-compiler-utils-2.5.2.tgz#a8d57e773354ab10e4742c7d6a8dd86184d4d7be"
|
||||
|
|
@ -5427,6 +5501,11 @@ lodash.sortby@^4.7.0:
|
|||
version "4.7.0"
|
||||
resolved "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
|
||||
|
||||
lodash.unescape@4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c"
|
||||
integrity sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=
|
||||
|
||||
lodash.uniq@^4.5.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
|
||||
|
|
@ -7193,6 +7272,11 @@ require-main-filename@^1.0.1:
|
|||
version "1.0.1"
|
||||
resolved "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1"
|
||||
|
||||
requireindex@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz#3463cdb22ee151902635aa6c9535d4de9c2ef1ef"
|
||||
integrity sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==
|
||||
|
||||
requires-port@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
|
||||
|
|
@ -7376,6 +7460,11 @@ selfsigned@^1.9.1:
|
|||
version "5.5.1"
|
||||
resolved "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477"
|
||||
|
||||
semver@5.5.0:
|
||||
version "5.5.0"
|
||||
resolved "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
|
||||
integrity sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==
|
||||
|
||||
semver@^5.5, semver@^5.6.0:
|
||||
version "5.6.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
|
||||
|
|
@ -8146,10 +8235,17 @@ ts-jest@^23.10.5:
|
|||
semver "^5.5"
|
||||
yargs-parser "10.x"
|
||||
|
||||
tslib@^1.9.0:
|
||||
tslib@^1.8.1, tslib@^1.9.0:
|
||||
version "1.9.3"
|
||||
resolved "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286"
|
||||
|
||||
tsutils@^3.7.0:
|
||||
version "3.9.1"
|
||||
resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.9.1.tgz#2a40dc742943c71eca6d5c1994fcf999956be387"
|
||||
integrity sha512-hrxVtLtPqQr//p8/msPT1X1UYXUjizqSit5d9AQ5k38TcV38NyecL5xODNxa73cLe/5sdiJ+w1FqzDhRBA/anA==
|
||||
dependencies:
|
||||
tslib "^1.8.1"
|
||||
|
||||
tty-browserify@0.0.0:
|
||||
version "0.0.0"
|
||||
resolved "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
|
||||
|
|
@ -8185,6 +8281,11 @@ typedarray@^0.0.6, typedarray@~0.0.5:
|
|||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||
|
||||
typescript@^3.3.3333:
|
||||
version "3.3.3333"
|
||||
resolved "https://registry.npmjs.org/typescript/-/typescript-3.3.3333.tgz#171b2c5af66c59e9431199117a3bcadc66fdcfd6"
|
||||
integrity sha512-JjSKsAfuHBE/fB2oZ8NxtRTk5iGcg6hkYXMnZ3Wc+b2RSqejEqTaem11mHASMnFilHrax3sLK0GDzcJrekZYLw==
|
||||
|
||||
uglify-js@^3.1.4:
|
||||
version "3.4.9"
|
||||
resolved "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz#af02f180c1207d76432e473ed24a28f4a782bae3"
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user