mirror of
https://github.com/marktext/marktext.git
synced 2025-05-03 00:01:19 +08:00
Fix tests and improve CI (#590)
* Fix tests and improve CI * Add xvfb to Linux - still problems with Mocha e2e tests * Disable Webpack Bundle Analyzer for testing * Use preinstalled yarn application on AppVeyor * Fix build failure with latest vue version * Remove "markdown-toc" * Fix application title unit test * Hide electron window during unit tests * Add basic muya unit tests * Dirty markdown-toc replacement * Update dependencies * Update eslint packages and configuration
This commit is contained in:
parent
fa394be978
commit
2ce05829d7
@ -172,13 +172,18 @@ const rendererConfig = {
|
|||||||
*/
|
*/
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
rendererConfig.plugins.push(
|
rendererConfig.plugins.push(
|
||||||
new BundleAnalyzerPlugin(),
|
|
||||||
new webpack.DefinePlugin({
|
new webpack.DefinePlugin({
|
||||||
'__static': `"${path.join(__dirname, '../static').replace(/\\/g, '\\\\')}"`
|
'__static': `"${path.join(__dirname, '../static').replace(/\\/g, '\\\\')}"`
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV !== 'production' && process.env.NODE_ENV !== 'test') {
|
||||||
|
rendererConfig.plugins.push(
|
||||||
|
new BundleAnalyzerPlugin()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adjust rendererConfig for production settings
|
* Adjust rendererConfig for production settings
|
||||||
*/
|
*/
|
||||||
|
17
.eslintrc.js
17
.eslintrc.js
@ -2,24 +2,35 @@ module.exports = {
|
|||||||
root: true,
|
root: true,
|
||||||
parser: 'babel-eslint',
|
parser: 'babel-eslint',
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
|
ecmaFeatures: {
|
||||||
|
impliedStrict: true
|
||||||
|
},
|
||||||
sourceType: 'module'
|
sourceType: 'module'
|
||||||
},
|
},
|
||||||
env: {
|
env: {
|
||||||
browser: true,
|
browser: true,
|
||||||
|
es6: true,
|
||||||
node: true
|
node: true
|
||||||
},
|
},
|
||||||
extends: 'standard',
|
extends: [
|
||||||
|
'standard',
|
||||||
|
'eslint:recommended',
|
||||||
|
'plugin:vue/base' // 'plugin:vue/essential'
|
||||||
|
],
|
||||||
globals: {
|
globals: {
|
||||||
__static: true
|
__static: true
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
'html'
|
'html',
|
||||||
|
'vue'
|
||||||
],
|
],
|
||||||
'rules': {
|
rules: {
|
||||||
// allow paren-less arrow functions
|
// allow paren-less arrow functions
|
||||||
'arrow-parens': 0,
|
'arrow-parens': 0,
|
||||||
// allow async-await
|
// allow async-await
|
||||||
'generator-star-spacing': 0,
|
'generator-star-spacing': 0,
|
||||||
|
// allow console
|
||||||
|
'no-console': 0,
|
||||||
// allow debugger during development
|
// allow debugger during development
|
||||||
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
|
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
|
||||||
}
|
}
|
||||||
|
32
.travis.yml
32
.travis.yml
@ -1,9 +1,4 @@
|
|||||||
# Commented sections below can be used to run tests on the CI server
|
|
||||||
# https://simulatedgreg.gitbooks.io/electron-vue/content/en/testing.html#on-the-subject-of-ci-testing
|
|
||||||
osx_image: xcode9.2
|
|
||||||
sudo: required
|
sudo: required
|
||||||
dist: trusty
|
|
||||||
|
|
||||||
language: node_js
|
language: node_js
|
||||||
node_js:
|
node_js:
|
||||||
- 8
|
- 8
|
||||||
@ -11,10 +6,12 @@ node_js:
|
|||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- os: osx
|
- os: osx
|
||||||
env: CC=clang CXX=clang++ npm_config_clang=1 MARKTEXT_IS_OFFICIAL_RELEASE=1
|
osx_image: xcode9.2
|
||||||
|
env: CC=clang CXX=clang++ npm_config_clang=1 MARKTEXT_IS_OFFICIAL_RELEASE=1 MARKTEXT_EXIT_ON_ERROR=1
|
||||||
compiler: clang
|
compiler: clang
|
||||||
- os: linux
|
- os: linux
|
||||||
env: CC=clang CXX=clang++ npm_config_clang=1 MARKTEXT_IS_OFFICIAL_RELEASE=1
|
dist: trusty
|
||||||
|
env: CC=clang CXX=clang++ npm_config_clang=1 MARKTEXT_IS_OFFICIAL_RELEASE=1 MARKTEXT_EXIT_ON_ERROR=1 DISPLAY=:99.0
|
||||||
compiler: clang
|
compiler: clang
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
@ -30,28 +27,19 @@ addons:
|
|||||||
- icnsutils
|
- icnsutils
|
||||||
- graphicsmagick
|
- graphicsmagick
|
||||||
- xz-utils
|
- xz-utils
|
||||||
#- xvfb
|
- xvfb
|
||||||
# atom/keyboard-layout
|
# atom/keyboard-layout
|
||||||
- libx11-dev
|
- libx11-dev
|
||||||
- libxkbfile-dev
|
- libxkbfile-dev
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
#- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get -qq update ; fi
|
|
||||||
#- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
|
|
||||||
# wget -qO - https://dl.winehq.org/wine-builds/Release.key | sudo apt-key add -;
|
|
||||||
# sudo apt-add-repository https://dl.winehq.org/wine-builds/ubuntu/;
|
|
||||||
# fi
|
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get -qq update ; fi
|
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get -qq update ; fi
|
||||||
#- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install --install-recommends winehq-stable ; fi
|
|
||||||
|
|
||||||
#- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update ; fi
|
#- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update ; fi
|
||||||
|
|
||||||
install:
|
install:
|
||||||
#- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then export DISPLAY=':99.0' ; fi
|
|
||||||
#- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & ; fi
|
|
||||||
- curl -o- -L https://yarnpkg.com/install.sh | bash
|
- curl -o- -L https://yarnpkg.com/install.sh | bash
|
||||||
- source ~/.bashrc
|
- source ~/.bashrc
|
||||||
- npm install -g xvfb-maybe
|
|
||||||
|
|
||||||
- $CC --version
|
- $CC --version
|
||||||
- $CXX --version
|
- $CXX --version
|
||||||
@ -61,13 +49,17 @@ install:
|
|||||||
- yarn
|
- yarn
|
||||||
|
|
||||||
script:
|
script:
|
||||||
#- xvfb-maybe node_modules/.bin/karma start test/unit/karma.conf.js
|
|
||||||
#- yarn run pack && xvfb-maybe node_modules/.bin/mocha test/e2e
|
|
||||||
- yarn run lint
|
- yarn run lint
|
||||||
|
|
||||||
|
# Unit and e2e tests
|
||||||
|
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then xvfb-run --server-args="-screen 0 1024x768x24" yarn run test ; fi
|
||||||
|
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then yarn run test ; fi
|
||||||
|
|
||||||
|
# Build binaries
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then yarn run release:linux ; fi
|
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then yarn run release:linux ; fi
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then yarn run release:mac ; fi
|
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then yarn run release:mac ; fi
|
||||||
|
|
||||||
# calculate checksums
|
# Calculate checksums
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sha256sum build/marktext-*-x64.tar.gz ; fi
|
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sha256sum build/marktext-*-x64.tar.gz ; fi
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sha256sum build/marktext-*-x86_64.AppImage ; fi
|
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sha256sum build/marktext-*-x86_64.AppImage ; fi
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then shasum -a 256 build/Mark\ Text-*-mac.zip ; fi
|
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then shasum -a 256 build/Mark\ Text-*-mac.zip ; fi
|
||||||
|
@ -14,6 +14,7 @@ skip_tags: true
|
|||||||
|
|
||||||
environment:
|
environment:
|
||||||
MARKTEXT_IS_OFFICIAL_RELEASE: 1
|
MARKTEXT_IS_OFFICIAL_RELEASE: 1
|
||||||
|
MARKTEXT_EXIT_ON_ERROR: 1
|
||||||
GH_TOKEN:
|
GH_TOKEN:
|
||||||
secure: Ki5AJWygDYhzMJxl0b0rDx3bhAYmar2aPdwVHiai9IigqsvZpWHLeI3qpTiiaOWL
|
secure: Ki5AJWygDYhzMJxl0b0rDx3bhAYmar2aPdwVHiai9IigqsvZpWHLeI3qpTiiaOWL
|
||||||
|
|
||||||
@ -22,7 +23,6 @@ init:
|
|||||||
|
|
||||||
install:
|
install:
|
||||||
- ps: Install-Product node 8 $env:PLATFORM
|
- ps: Install-Product node 8 $env:PLATFORM
|
||||||
- choco install yarn --ignore-dependencies
|
|
||||||
|
|
||||||
- node --version
|
- node --version
|
||||||
- npm --version
|
- npm --version
|
||||||
@ -38,6 +38,8 @@ cache:
|
|||||||
|
|
||||||
build_script:
|
build_script:
|
||||||
- yarn run lint
|
- yarn run lint
|
||||||
|
- yarn run test
|
||||||
|
|
||||||
- yarn run release:win
|
- yarn run release:win
|
||||||
|
|
||||||
# calculate checksums
|
# calculate checksums
|
||||||
@ -46,4 +48,4 @@ build_script:
|
|||||||
|
|
||||||
test: off
|
test: off
|
||||||
# test_script:
|
# test_script:
|
||||||
# - yarn test
|
# - yarn run test
|
6140
package-lock.json
generated
6140
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
55
package.json
55
package.json
@ -23,7 +23,7 @@
|
|||||||
"pack:main": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.main.config.js",
|
"pack:main": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.main.config.js",
|
||||||
"pack:renderer": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.renderer.config.js",
|
"pack:renderer": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.renderer.config.js",
|
||||||
"test": "npm run unit && npm run e2e",
|
"test": "npm run unit && npm run e2e",
|
||||||
"unit": "karma start test/unit/karma.conf.js",
|
"unit": "cross-env NODE_ENV=test karma start test/unit/karma.conf.js",
|
||||||
"postinstall": "npm run rebuild && npm run lint:fix",
|
"postinstall": "npm run rebuild && npm run lint:fix",
|
||||||
"build:muya": "cd src/muya && webpack --progress --colors --config webpack.config.js",
|
"build:muya": "cd src/muya && webpack --progress --colors --config webpack.config.js",
|
||||||
"release:muya": "npm run build:muya && cd src/muya && npm publish",
|
"release:muya": "npm run build:muya && cd src/muya && npm publish",
|
||||||
@ -135,9 +135,10 @@
|
|||||||
"chokidar": "^2.0.4",
|
"chokidar": "^2.0.4",
|
||||||
"codemirror": "^5.42.0",
|
"codemirror": "^5.42.0",
|
||||||
"command-exists": "^1.2.8",
|
"command-exists": "^1.2.8",
|
||||||
|
"diacritics-map": "^0.1.0",
|
||||||
"dompurify": "^1.0.8",
|
"dompurify": "^1.0.8",
|
||||||
"electron-is-accelerator": "^0.1.2",
|
"electron-is-accelerator": "^0.1.2",
|
||||||
"element-resize-detector": "^1.1.14",
|
"element-resize-detector": "^1.2.0",
|
||||||
"element-ui": "^2.4.11",
|
"element-ui": "^2.4.11",
|
||||||
"file-icons-js": "^1.0.3",
|
"file-icons-js": "^1.0.3",
|
||||||
"flowchart.js": "^1.11.3",
|
"flowchart.js": "^1.11.3",
|
||||||
@ -147,9 +148,8 @@
|
|||||||
"html-tags": "^2.0.0",
|
"html-tags": "^2.0.0",
|
||||||
"katex": "^0.10.0",
|
"katex": "^0.10.0",
|
||||||
"keyboard-layout": "^2.0.14",
|
"keyboard-layout": "^2.0.14",
|
||||||
"markdown-toc": "^1.2.0",
|
|
||||||
"mermaid": "^8.0.0-rc.8",
|
"mermaid": "^8.0.0-rc.8",
|
||||||
"popper.js": "^1.14.5",
|
"popper.js": "^1.14.6",
|
||||||
"prismjs2": "^1.15.0",
|
"prismjs2": "^1.15.0",
|
||||||
"snabbdom": "^0.7.2",
|
"snabbdom": "^0.7.2",
|
||||||
"snabbdom-to-html": "^5.1.1",
|
"snabbdom-to-html": "^5.1.1",
|
||||||
@ -157,16 +157,16 @@
|
|||||||
"turndown": "^5.0.1",
|
"turndown": "^5.0.1",
|
||||||
"turndown-plugin-gfm": "^1.0.2",
|
"turndown-plugin-gfm": "^1.0.2",
|
||||||
"underscore": "^1.9.1",
|
"underscore": "^1.9.1",
|
||||||
"vega": "^4.3.0",
|
"vega": "^4.4.0",
|
||||||
"vega-embed": "^3.24.2",
|
"vega-embed": "^3.26.1",
|
||||||
"vega-lite": "^3.0.0-rc8",
|
"vega-lite": "^3.0.0-rc10",
|
||||||
"vue": "^2.5.21",
|
"vue": "^2.5.21",
|
||||||
"vue-electron": "^1.0.6",
|
"vue-electron": "^1.0.6",
|
||||||
"vuex": "^3.0.1"
|
"vuex": "^3.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"babel-core": "^6.26.3",
|
"babel-core": "^6.26.3",
|
||||||
"babel-eslint": "^8.2.6",
|
"babel-eslint": "^10.0.1",
|
||||||
"babel-loader": "^7.1.5",
|
"babel-loader": "^7.1.5",
|
||||||
"babel-plugin-component": "^1.1.1",
|
"babel-plugin-component": "^1.1.1",
|
||||||
"babel-plugin-istanbul": "^5.1.0",
|
"babel-plugin-istanbul": "^5.1.0",
|
||||||
@ -182,42 +182,43 @@
|
|||||||
"css-loader": "^1.0.1",
|
"css-loader": "^1.0.1",
|
||||||
"del": "^3.0.0",
|
"del": "^3.0.0",
|
||||||
"devtron": "^1.4.0",
|
"devtron": "^1.4.0",
|
||||||
"electron": "^3.0.10",
|
"electron": "^3.0.12",
|
||||||
"electron-builder": "^20.38.1",
|
"electron-builder": "^20.38.3",
|
||||||
"electron-debug": "^2.0.0",
|
"electron-debug": "^2.0.0",
|
||||||
"electron-devtools-installer": "^2.2.4",
|
"electron-devtools-installer": "^2.2.4",
|
||||||
"electron-rebuild": "^1.8.2",
|
"electron-rebuild": "^1.8.2",
|
||||||
"electron-updater": "^4.0.5",
|
"electron-updater": "^4.0.6",
|
||||||
"electron-window-state": "^5.0.2",
|
"electron-window-state": "^5.0.3",
|
||||||
"eslint": "^4.19.1",
|
"eslint": "^5.10.0",
|
||||||
"eslint-config-standard": "^11.0.0",
|
"eslint-config-standard": "^12.0.0",
|
||||||
"eslint-friendly-formatter": "^4.0.1",
|
"eslint-friendly-formatter": "^4.0.1",
|
||||||
"eslint-loader": "^2.1.1",
|
"eslint-loader": "^2.1.1",
|
||||||
"eslint-plugin-html": "^4.0.6",
|
"eslint-plugin-html": "^4.0.6",
|
||||||
"eslint-plugin-import": "^2.14.0",
|
"eslint-plugin-import": "^2.14.0",
|
||||||
"eslint-plugin-node": "^6.0.1",
|
"eslint-plugin-node": "^8.0.0",
|
||||||
"eslint-plugin-promise": "^3.8.0",
|
"eslint-plugin-promise": "^4.0.1",
|
||||||
"eslint-plugin-standard": "^3.1.0",
|
"eslint-plugin-standard": "^4.0.0",
|
||||||
|
"eslint-plugin-vue": "^5.0.0",
|
||||||
"file-loader": "^2.0.0",
|
"file-loader": "^2.0.0",
|
||||||
"git-revision-webpack-plugin": "^3.0.3",
|
"git-revision-webpack-plugin": "^3.0.3",
|
||||||
"html-webpack-plugin": "^3.2.0",
|
"html-webpack-plugin": "^3.2.0",
|
||||||
"inject-loader": "^4.0.1",
|
"inject-loader": "^4.0.1",
|
||||||
"karma": "^1.3.0",
|
"karma": "^3.1.3",
|
||||||
"karma-chai": "^0.1.0",
|
"karma-chai": "^0.1.0",
|
||||||
"karma-coverage": "^1.1.1",
|
"karma-coverage": "^1.1.1",
|
||||||
"karma-electron": "^5.3.0",
|
"karma-electron": "^6.0.0",
|
||||||
"karma-mocha": "^1.2.0",
|
"karma-mocha": "^1.2.0",
|
||||||
"karma-sourcemap-loader": "^0.3.7",
|
"karma-sourcemap-loader": "^0.3.7",
|
||||||
"karma-spec-reporter": "^0.0.31",
|
"karma-spec-reporter": "0.0.32",
|
||||||
"karma-webpack": "^2.0.1",
|
"karma-webpack": "^3.0.5",
|
||||||
"mini-css-extract-plugin": "^0.4.5",
|
"mini-css-extract-plugin": "^0.4.5",
|
||||||
"mocha": "^3.0.2",
|
"mocha": "^5.2.0",
|
||||||
"multispinner": "^0.2.1",
|
"multispinner": "^0.2.1",
|
||||||
"node-loader": "^0.6.0",
|
"node-loader": "^0.6.0",
|
||||||
"postcss-loader": "^3.0.0",
|
"postcss-loader": "^3.0.0",
|
||||||
"postcss-preset-env": "^6.4.0",
|
"postcss-preset-env": "^6.5.0",
|
||||||
"require-dir": "^1.1.0",
|
"require-dir": "^1.2.0",
|
||||||
"spectron": "^4.0.0",
|
"spectron": "^5.0.0",
|
||||||
"style-loader": "^0.23.1",
|
"style-loader": "^0.23.1",
|
||||||
"svg-sprite-loader": "^4.1.3",
|
"svg-sprite-loader": "^4.1.3",
|
||||||
"svgo": "^1.1.1",
|
"svgo": "^1.1.1",
|
||||||
@ -228,11 +229,11 @@
|
|||||||
"vue-loader": "^15.4.2",
|
"vue-loader": "^15.4.2",
|
||||||
"vue-style-loader": "^4.1.2",
|
"vue-style-loader": "^4.1.2",
|
||||||
"vue-template-compiler": "^2.5.21",
|
"vue-template-compiler": "^2.5.21",
|
||||||
"webpack": "^4.26.0",
|
"webpack": "^4.27.1",
|
||||||
"webpack-bundle-analyzer": "^3.0.3",
|
"webpack-bundle-analyzer": "^3.0.3",
|
||||||
"webpack-cli": "^3.1.2",
|
"webpack-cli": "^3.1.2",
|
||||||
"webpack-dev-server": "^3.1.10",
|
"webpack-dev-server": "^3.1.10",
|
||||||
"webpack-hot-middleware": "^2.24.3",
|
"webpack-hot-middleware": "^2.24.3",
|
||||||
"webpack-merge": "^4.1.4"
|
"webpack-merge": "^4.1.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,10 +66,11 @@ const handleError = (title, error) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case 1:
|
case 1: {
|
||||||
clipboard.writeText(`${title}\n${stack}`)
|
clipboard.writeText(`${title}\n${stack}`)
|
||||||
break
|
break
|
||||||
case 2:
|
}
|
||||||
|
case 2: {
|
||||||
const issueTitle = message ? `Unexpected error: ${message}` : title
|
const issueTitle = message ? `Unexpected error: ${message}` : title
|
||||||
const gitInfo = global.MARKTEXT_IS_OFFICIAL_RELEASE ? `(${global.MARKTEXT_GIT_INFO} - git)` : global.MARKTEXT_GIT_INFO
|
const gitInfo = global.MARKTEXT_IS_OFFICIAL_RELEASE ? `(${global.MARKTEXT_GIT_INFO} - git)` : global.MARKTEXT_GIT_INFO
|
||||||
createAndOpenGitHubIssueUrl(
|
createAndOpenGitHubIssueUrl(
|
||||||
@ -89,6 +90,7 @@ ${title}.
|
|||||||
Mark Text: ${app.getVersion()} (${gitInfo})
|
Mark Text: ${app.getVersion()} (${gitInfo})
|
||||||
Operating system: ${process.platform}`)
|
Operating system: ${process.platform}`)
|
||||||
break
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// error during Electron initialization
|
// error during Electron initialization
|
||||||
|
@ -307,7 +307,8 @@ const enterCtrl = ContentState => {
|
|||||||
let newBlock
|
let newBlock
|
||||||
|
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case left !== 0 && right !== 0: // cursor in the middle
|
case left !== 0 && right !== 0: {
|
||||||
|
// cursor in the middle
|
||||||
let { pre, post } = selection.chopHtmlByCursor(paragraph)
|
let { pre, post } = selection.chopHtmlByCursor(paragraph)
|
||||||
|
|
||||||
if (/^h\d$/.test(block.type)) {
|
if (/^h\d$/.test(block.type)) {
|
||||||
@ -335,10 +336,14 @@ const enterCtrl = ContentState => {
|
|||||||
}
|
}
|
||||||
this.insertAfter(newBlock, block)
|
this.insertAfter(newBlock, block)
|
||||||
break
|
break
|
||||||
case left === 0 && right === 0: // paragraph is empty
|
}
|
||||||
return this.enterInEmptyParagraph(block)
|
case left === 0 && right === 0: {
|
||||||
case left !== 0 && right === 0: // cursor at end of paragraph
|
// paragraph is empty
|
||||||
case left === 0 && right !== 0: // cursor at begin of paragraph
|
return this.enterInEmptyParagraph(block)
|
||||||
|
}
|
||||||
|
case left !== 0 && right === 0:
|
||||||
|
case left === 0 && right !== 0: {
|
||||||
|
// cursor at end of paragraph or at begin of paragraph
|
||||||
if (type === 'li') {
|
if (type === 'li') {
|
||||||
if (block.listItemType === 'task') {
|
if (block.listItemType === 'task') {
|
||||||
const { checked } = block.children[0]
|
const { checked } = block.children[0]
|
||||||
@ -368,10 +373,12 @@ const enterCtrl = ContentState => {
|
|||||||
this.insertAfter(newBlock, block)
|
this.insertAfter(newBlock, block)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
default:
|
}
|
||||||
|
default: {
|
||||||
newBlock = this.createBlockP()
|
newBlock = this.createBlockP()
|
||||||
this.insertAfter(newBlock, block)
|
this.insertAfter(newBlock, block)
|
||||||
break
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const getParagraphBlock = block => {
|
const getParagraphBlock = block => {
|
||||||
|
@ -49,22 +49,25 @@ const clearFormat = (token, { start, end }) => {
|
|||||||
case 'strong':
|
case 'strong':
|
||||||
case 'del':
|
case 'del':
|
||||||
case 'em':
|
case 'em':
|
||||||
case 'link':
|
case 'link': {
|
||||||
const { parent } = token
|
const { parent } = token
|
||||||
const index = parent.indexOf(token)
|
const index = parent.indexOf(token)
|
||||||
parent.splice(index, 1, ...token.children)
|
parent.splice(index, 1, ...token.children)
|
||||||
break
|
break
|
||||||
case 'image':
|
}
|
||||||
|
case 'image': {
|
||||||
token.type = 'text'
|
token.type = 'text'
|
||||||
token.raw = token.alt
|
token.raw = token.alt
|
||||||
delete token.marker
|
delete token.marker
|
||||||
delete token.src
|
delete token.src
|
||||||
break
|
break
|
||||||
case 'inline_code':
|
}
|
||||||
|
case 'inline_code': {
|
||||||
token.type = 'text'
|
token.type = 'text'
|
||||||
token.raw = token.content
|
token.raw = token.content
|
||||||
delete token.marker
|
delete token.marker
|
||||||
break
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ const htmlBlock = ContentState => {
|
|||||||
const htmlBlock = this.getParent(codeBlockContainer)
|
const htmlBlock = this.getParent(codeBlockContainer)
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'delete':
|
case 'delete': {
|
||||||
htmlBlock.type = 'p'
|
htmlBlock.type = 'p'
|
||||||
htmlBlock.text = ''
|
htmlBlock.text = ''
|
||||||
htmlBlock.children = []
|
htmlBlock.children = []
|
||||||
@ -69,6 +69,7 @@ const htmlBlock = ContentState => {
|
|||||||
}
|
}
|
||||||
this.partialRender()
|
this.partialRender()
|
||||||
break
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,7 +241,7 @@ const pasteCtrl = ContentState => {
|
|||||||
lastBlock.text += cacheText
|
lastBlock.text += cacheText
|
||||||
|
|
||||||
switch (pasteType) {
|
switch (pasteType) {
|
||||||
case 'MERGE':
|
case 'MERGE': {
|
||||||
if (LIST_REG.test(firstFragment.type)) {
|
if (LIST_REG.test(firstFragment.type)) {
|
||||||
const listItems = firstFragment.children
|
const listItems = firstFragment.children
|
||||||
const firstListItem = listItems[0]
|
const firstListItem = listItems[0]
|
||||||
@ -295,8 +295,8 @@ const pasteCtrl = ContentState => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
}
|
||||||
case 'NEWLINE':
|
case 'NEWLINE': {
|
||||||
let target = startBlock.type === 'span' ? parent : startBlock
|
let target = startBlock.type === 'span' ? parent : startBlock
|
||||||
stateFragments.forEach(block => {
|
stateFragments.forEach(block => {
|
||||||
this.insertAfter(block, target)
|
this.insertAfter(block, target)
|
||||||
@ -307,8 +307,10 @@ const pasteCtrl = ContentState => {
|
|||||||
if (this.isOnlyChild(startBlock) && startBlock.type === 'span') this.removeBlock(parent)
|
if (this.isOnlyChild(startBlock) && startBlock.type === 'span') this.removeBlock(parent)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
default:
|
}
|
||||||
|
default: {
|
||||||
throw new Error('unknown paste type')
|
throw new Error('unknown paste type')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// step 3: set cursor and render
|
// step 3: set cursor and render
|
||||||
let cursorBlock = this.getBlock(key)
|
let cursorBlock = this.getBlock(key)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import katex from 'katex'
|
import katex from 'katex'
|
||||||
import mermaid from 'mermaid'
|
import mermaid from 'mermaid'
|
||||||
import prism, { loadedCache } from '../../../prism/'
|
import prism, { loadedCache } from '../../../prism/'
|
||||||
|
import { slugify } from '../../../utils/dirtyToc'
|
||||||
import { CLASS_OR_ID, DEVICE_MEMORY, isInElectron, PREVIEW_DOMPURIFY_CONFIG, HAS_TEXT_BLOCK_REG } from '../../../config'
|
import { CLASS_OR_ID, DEVICE_MEMORY, isInElectron, PREVIEW_DOMPURIFY_CONFIG, HAS_TEXT_BLOCK_REG } from '../../../config'
|
||||||
import { tokenizer } from '../../parse'
|
import { tokenizer } from '../../parse'
|
||||||
import { snakeToCamel, sanitize, escapeHtml, getLongUniqueId } from '../../../utils'
|
import { snakeToCamel, sanitize, escapeHtml, getLongUniqueId } from '../../../utils'
|
||||||
@ -186,9 +187,11 @@ export default function renderLeafBlock (block, cursor, activeBlocks, matches, u
|
|||||||
]
|
]
|
||||||
} else if (/^h/.test(type)) {
|
} else if (/^h/.test(type)) {
|
||||||
if (/^h\d$/.test(type)) {
|
if (/^h\d$/.test(type)) {
|
||||||
|
// TODO: This should be the best place to create and update the TOC.
|
||||||
|
// Cache `block.key` and title and update only if necessary.
|
||||||
Object.assign(data.dataset, {
|
Object.assign(data.dataset, {
|
||||||
head: type,
|
head: type,
|
||||||
id: isInElectron ? require('markdown-toc').slugify(text.replace(/^#+\s(.*)/, (_, p1) => p1)) : ''
|
id: isInElectron ? slugify(text.replace(/^#+\s(.*)/, (_, p1) => p1)) : ''
|
||||||
})
|
})
|
||||||
selector += `.${headingStyle}`
|
selector += `.${headingStyle}`
|
||||||
}
|
}
|
||||||
|
39
src/muya/lib/utils/dirtyToc.js
Normal file
39
src/muya/lib/utils/dirtyToc.js
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { Lexer } from '../parser/marked'
|
||||||
|
import diacritics from 'diacritics-map'
|
||||||
|
|
||||||
|
export const getTocFromMarkdown = markdown => {
|
||||||
|
const tokens = new Lexer({ disableInline: true }).lex(markdown)
|
||||||
|
const toc = []
|
||||||
|
let token = null
|
||||||
|
while ((token = tokens.shift())) {
|
||||||
|
switch (token.type) {
|
||||||
|
case 'heading': {
|
||||||
|
const { depth, text } = token
|
||||||
|
toc.push({
|
||||||
|
content: text,
|
||||||
|
lvl: depth,
|
||||||
|
slug: slugify(text)
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return toc
|
||||||
|
}
|
||||||
|
|
||||||
|
export const slugify = str => {
|
||||||
|
str = str.replace(/^\s+|\s+$/g, '').toLowerCase()
|
||||||
|
|
||||||
|
// replace accents
|
||||||
|
str = str.replace(/[À-ž]/g, c => {
|
||||||
|
return diacritics[c] || c
|
||||||
|
})
|
||||||
|
|
||||||
|
return str
|
||||||
|
.replace(/[^a-z0-9 -]/g, '') // remove invalid chars
|
||||||
|
.replace(/\t/g, '--') // collapse tabs and replace by --
|
||||||
|
.replace(/\s+/g, '-') // collapse whitespace and replace by -
|
||||||
|
.replace(/^-+/, '') // trim - from start of text
|
||||||
|
.replace(/-+$/, '') // trim - from end of text
|
||||||
|
.replace(/-+/g, '-') // collapse dashes
|
||||||
|
}
|
@ -26,52 +26,56 @@ class ExportMarkdown {
|
|||||||
|
|
||||||
for (const block of blocks) {
|
for (const block of blocks) {
|
||||||
switch (block.type) {
|
switch (block.type) {
|
||||||
case 'p':
|
case 'p': {
|
||||||
this.insertLineBreak(result, indent, true)
|
this.insertLineBreak(result, indent, true)
|
||||||
result.push(this.translateBlocks2Markdown(block.children, indent))
|
result.push(this.translateBlocks2Markdown(block.children, indent))
|
||||||
break
|
break
|
||||||
|
}
|
||||||
case 'span':
|
case 'span': {
|
||||||
result.push(this.normalizeParagraphText(block, indent))
|
result.push(this.normalizeParagraphText(block, indent))
|
||||||
break
|
break
|
||||||
|
}
|
||||||
case 'hr':
|
case 'hr': {
|
||||||
this.insertLineBreak(result, indent, true)
|
this.insertLineBreak(result, indent, true)
|
||||||
result.push(this.normalizeParagraphText(block, indent))
|
result.push(this.normalizeParagraphText(block, indent))
|
||||||
break
|
break
|
||||||
|
}
|
||||||
case 'h1':
|
case 'h1':
|
||||||
case 'h2':
|
case 'h2':
|
||||||
case 'h3':
|
case 'h3':
|
||||||
case 'h4':
|
case 'h4':
|
||||||
case 'h5':
|
case 'h5':
|
||||||
case 'h6':
|
case 'h6': {
|
||||||
this.insertLineBreak(result, indent, true)
|
this.insertLineBreak(result, indent, true)
|
||||||
result.push(this.normalizeHeaderText(block, indent))
|
result.push(this.normalizeHeaderText(block, indent))
|
||||||
break
|
break
|
||||||
|
}
|
||||||
case 'figure':
|
case 'figure': {
|
||||||
this.insertLineBreak(result, indent, true)
|
this.insertLineBreak(result, indent, true)
|
||||||
switch (block.functionType) {
|
switch (block.functionType) {
|
||||||
case 'table':
|
case 'table': {
|
||||||
const table = block.children[1]
|
const table = block.children[1]
|
||||||
result.push(this.normalizeTable(table, indent))
|
result.push(this.normalizeTable(table, indent))
|
||||||
break
|
break
|
||||||
case 'html':
|
}
|
||||||
|
case 'html': {
|
||||||
result.push(this.normalizeHTML(block, indent))
|
result.push(this.normalizeHTML(block, indent))
|
||||||
break
|
break
|
||||||
case 'multiplemath':
|
}
|
||||||
|
case 'multiplemath': {
|
||||||
result.push(this.normalizeMultipleMath(block, indent))
|
result.push(this.normalizeMultipleMath(block, indent))
|
||||||
break
|
break
|
||||||
|
}
|
||||||
case 'mermaid':
|
case 'mermaid':
|
||||||
case 'flowchart':
|
case 'flowchart':
|
||||||
case 'sequence':
|
case 'sequence':
|
||||||
case 'vega-lite':
|
case 'vega-lite': {
|
||||||
result.push(this.normalizeContainer(block, indent))
|
result.push(this.normalizeContainer(block, indent))
|
||||||
break
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
}
|
||||||
case 'li': {
|
case 'li': {
|
||||||
const insertNewLine = block.isLooseListItem
|
const insertNewLine = block.isLooseListItem
|
||||||
|
|
||||||
@ -83,7 +87,6 @@ class ExportMarkdown {
|
|||||||
this.isLooseParentList = true
|
this.isLooseParentList = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'ul': {
|
case 'ul': {
|
||||||
const insertNewLine = this.isLooseParentList
|
const insertNewLine = this.isLooseParentList
|
||||||
this.isLooseParentList = true
|
this.isLooseParentList = true
|
||||||
@ -94,7 +97,6 @@ class ExportMarkdown {
|
|||||||
this.listType.pop()
|
this.listType.pop()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'ol': {
|
case 'ol': {
|
||||||
const insertNewLine = this.isLooseParentList
|
const insertNewLine = this.isLooseParentList
|
||||||
this.isLooseParentList = true
|
this.isLooseParentList = true
|
||||||
@ -106,8 +108,7 @@ class ExportMarkdown {
|
|||||||
this.listType.pop()
|
this.listType.pop()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
case 'pre': {
|
||||||
case 'pre':
|
|
||||||
this.insertLineBreak(result, indent, true)
|
this.insertLineBreak(result, indent, true)
|
||||||
if (block.functionType === 'frontmatter') {
|
if (block.functionType === 'frontmatter') {
|
||||||
result.push(this.normalizeFrontMatter(block, indent))
|
result.push(this.normalizeFrontMatter(block, indent))
|
||||||
@ -115,14 +116,16 @@ class ExportMarkdown {
|
|||||||
result.push(this.normalizeCodeBlock(block, indent))
|
result.push(this.normalizeCodeBlock(block, indent))
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
}
|
||||||
case 'blockquote':
|
case 'blockquote': {
|
||||||
this.insertLineBreak(result, indent, true)
|
this.insertLineBreak(result, indent, true)
|
||||||
result.push(this.normalizeBlockquote(block, indent))
|
result.push(this.normalizeBlockquote(block, indent))
|
||||||
break
|
break
|
||||||
default:
|
}
|
||||||
|
default: {
|
||||||
console.log(block.type)
|
console.log(block.type)
|
||||||
break
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result.join('')
|
return result.join('')
|
||||||
|
@ -157,13 +157,14 @@
|
|||||||
handleInput (event) {
|
handleInput (event) {
|
||||||
let historyIndex = this.historyIndex
|
let historyIndex = this.historyIndex
|
||||||
switch (event.key) {
|
switch (event.key) {
|
||||||
case 'Enter':
|
case 'Enter': {
|
||||||
const query = this.historyIndex !== -1 ? this.history[this.historyIndex] : this.query
|
const query = this.historyIndex !== -1 ? this.history[this.historyIndex] : this.query
|
||||||
if (!this.aiLoading) {
|
if (!this.aiLoading) {
|
||||||
this.search(query)
|
this.search(query)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 'ArrowUp':
|
}
|
||||||
|
case 'ArrowUp': {
|
||||||
historyIndex = historyIndex - 1
|
historyIndex = historyIndex - 1
|
||||||
if (historyIndex === -1 || historyIndex === -2) {
|
if (historyIndex === -1 || historyIndex === -2) {
|
||||||
this.historyIndex = this.history.length - 1
|
this.historyIndex = this.history.length - 1
|
||||||
@ -171,7 +172,8 @@
|
|||||||
this.historyIndex = historyIndex
|
this.historyIndex = historyIndex
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 'ArrowDown':
|
}
|
||||||
|
case 'ArrowDown': {
|
||||||
historyIndex = historyIndex + 1
|
historyIndex = historyIndex + 1
|
||||||
if (historyIndex >= this.history.length) {
|
if (historyIndex >= this.history.length) {
|
||||||
this.historyIndex = 0
|
this.historyIndex = 0
|
||||||
@ -179,6 +181,7 @@
|
|||||||
this.historyIndex = historyIndex
|
this.historyIndex = historyIndex
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleShowAiDou () {
|
handleShowAiDou () {
|
||||||
|
@ -36,7 +36,7 @@ export const showContextMenu = (event, { start, end }) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
;[CUT, COPY, COPY_AS_HTML, COPY_AS_MARKDOWN].forEach(item => {
|
[CUT, COPY, COPY_AS_HTML, COPY_AS_MARKDOWN].forEach(item => {
|
||||||
item.enabled = !disableCutAndCopy
|
item.enabled = !disableCutAndCopy
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -5,7 +5,9 @@ import { hasKeys } from '../util'
|
|||||||
import { getOptionsFromState, getSingleFileState, getBlankFileState } from './help'
|
import { getOptionsFromState, getSingleFileState, getBlankFileState } from './help'
|
||||||
import notice from '../services/notification'
|
import notice from '../services/notification'
|
||||||
|
|
||||||
const toc = require('markdown-toc')
|
// HACK: When rewriting muya, create and update muya's TOC during heading parsing and pass it to the renderer process.
|
||||||
|
import { getTocFromMarkdown } from 'muya/lib/utils/dirtyToc'
|
||||||
|
|
||||||
const state = {
|
const state = {
|
||||||
lineEnding: 'lf',
|
lineEnding: 'lf',
|
||||||
currentFile: {},
|
currentFile: {},
|
||||||
@ -15,8 +17,8 @@ const state = {
|
|||||||
|
|
||||||
const getters = {
|
const getters = {
|
||||||
toc: state => {
|
toc: state => {
|
||||||
const { currentFile } = state
|
const { markdown } = state.currentFile
|
||||||
return toc(currentFile.markdown).json
|
return getTocFromMarkdown(markdown)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,15 +260,13 @@ const actions = {
|
|||||||
|
|
||||||
LISTEN_FOR_SAVE_CLOSE ({ commit, state }) {
|
LISTEN_FOR_SAVE_CLOSE ({ commit, state }) {
|
||||||
ipcRenderer.on('AGANI::save-all-response', (e, { err, data }) => {
|
ipcRenderer.on('AGANI::save-all-response', (e, { err, data }) => {
|
||||||
if (err) {
|
if (!err && Array.isArray(data)) {
|
||||||
} else if (Array.isArray(data)) {
|
|
||||||
const toBeClosedTabs = [...state.tabs.filter(f => f.isSaved), ...data]
|
const toBeClosedTabs = [...state.tabs.filter(f => f.isSaved), ...data]
|
||||||
commit('CLOSE_TABS', toBeClosedTabs)
|
commit('CLOSE_TABS', toBeClosedTabs)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
ipcRenderer.on('AGANI::save-single-response', (e, { err, data }) => {
|
ipcRenderer.on('AGANI::save-single-response', (e, { err, data }) => {
|
||||||
if (err) {
|
if (!err && Array.isArray(data) && data.length) {
|
||||||
} else if (Array.isArray(data) && data.length) {
|
|
||||||
commit('CLOSE_TABS', data)
|
commit('CLOSE_TABS', data)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -119,7 +119,7 @@ const actions = {
|
|||||||
LISTEN_FOR_UPDATE_PROJECT ({ commit, state, dispatch }) {
|
LISTEN_FOR_UPDATE_PROJECT ({ commit, state, dispatch }) {
|
||||||
ipcRenderer.on('AGANI::update-object-tree', (e, { type, change }) => {
|
ipcRenderer.on('AGANI::update-object-tree', (e, { type, change }) => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'add':
|
case 'add': {
|
||||||
const { pathname, data, isMarkdown } = change
|
const { pathname, data, isMarkdown } = change
|
||||||
commit('ADD_FILE', change)
|
commit('ADD_FILE', change)
|
||||||
if (isMarkdown && state.newFileNameCache && pathname === state.newFileNameCache) {
|
if (isMarkdown && state.newFileNameCache && pathname === state.newFileNameCache) {
|
||||||
@ -128,6 +128,7 @@ const actions = {
|
|||||||
commit('SET_NEWFILENAME', '')
|
commit('SET_NEWFILENAME', '')
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
}
|
||||||
case 'unlink':
|
case 'unlink':
|
||||||
commit('UNLINK_FILE', change)
|
commit('UNLINK_FILE', change)
|
||||||
commit('SET_SAVE_STATUS_WHEN_REMOVE', change)
|
commit('SET_SAVE_STATUS_WHEN_REMOVE', change)
|
||||||
|
@ -7,7 +7,8 @@ describe('Launch', function () {
|
|||||||
it('shows the proper application title', function () {
|
it('shows the proper application title', function () {
|
||||||
return this.app.client.getTitle()
|
return this.app.client.getTitle()
|
||||||
.then(title => {
|
.then(title => {
|
||||||
expect(title).to.equal('aganippe')
|
const expectedTitle = process.platform === 'darwin' ? 'Mark Text' : 'Untitled-1'
|
||||||
|
expect(title).to.equal(expectedTitle)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -3,21 +3,20 @@ import { Application } from 'spectron'
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
afterEach () {
|
afterEach () {
|
||||||
this.timeout(10000)
|
this.timeout(20000)
|
||||||
|
|
||||||
if (this.app && this.app.isRunning()) {
|
if (this.app && this.app.isRunning()) {
|
||||||
return this.app.stop()
|
return this.app.stop()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeEach () {
|
beforeEach () {
|
||||||
this.timeout(10000)
|
this.timeout(20000)
|
||||||
this.app = new Application({
|
this.app = new Application({
|
||||||
path: electron,
|
path: electron,
|
||||||
args: ['dist/electron/main.js'],
|
args: ['dist/electron/main.js'],
|
||||||
startTimeout: 10000,
|
startTimeout: 20000,
|
||||||
waitTimeout: 10000
|
waitTimeout: 20000
|
||||||
})
|
})
|
||||||
|
|
||||||
return this.app.start()
|
return this.app.start()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
28
test/unit/data/common/BasicTextFormatting.md
Normal file
28
test/unit/data/common/BasicTextFormatting.md
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# Basic Text Formatting
|
||||||
|
|
||||||
|
*this is in italic* and _so is this_
|
||||||
|
|
||||||
|
**this is in bold** and __so is this__
|
||||||
|
|
||||||
|
***this is bold and italic*** and ___so is this___
|
||||||
|
|
||||||
|
<b>this will be bold</b>
|
||||||
|
|
||||||
|
<i>this will be bold</i>
|
||||||
|
|
||||||
|
<s>this is strike through text</s>
|
||||||
|
|
||||||
|
## Paragraph
|
||||||
|
|
||||||
|
A two trailing spaces and a new line
|
||||||
|
makes a line break.
|
||||||
|
|
||||||
|
Two new lines make a new paragraph.
|
||||||
|
|
||||||
|
## Failing Tests
|
||||||
|
|
||||||
|
```
|
||||||
|
So _a_ single _word_ followed _b_y _a_nother
|
||||||
|
|
||||||
|
So __a__ single __word__ followed __b__y __a__nother
|
||||||
|
```
|
46
test/unit/data/common/Blockquotes.md
Normal file
46
test/unit/data/common/Blockquotes.md
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# Blockquotes
|
||||||
|
|
||||||
|
> Use it if you're quoting a person, a song or whatever.
|
||||||
|
|
||||||
|
foo
|
||||||
|
|
||||||
|
> Lorem Ipsum is simply dummy text of the printing and typesetting industry.
|
||||||
|
> Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.
|
||||||
|
> It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged.
|
||||||
|
|
||||||
|
- foo
|
||||||
|
- > bar
|
||||||
|
- baz
|
||||||
|
|
||||||
|
## Failing Tests
|
||||||
|
|
||||||
|
```
|
||||||
|
> You can use *italic* or lists inside them also.
|
||||||
|
And just like with other paragraphs,
|
||||||
|
all of these lines are still
|
||||||
|
part of the blockquote, even without the > character in front.
|
||||||
|
|
||||||
|
To end the blockquote, just put a blank line before the following
|
||||||
|
paragraph.
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
> - 1
|
||||||
|
> - 1
|
||||||
|
> - 1
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
* foo
|
||||||
|
|
||||||
|
> This is a blockquote
|
||||||
|
> inside a list item.
|
||||||
|
|
||||||
|
* bar`
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
> Use it if you're quoting a person, a song or whatever.
|
||||||
|
|
||||||
|
> You can use *italic* or lists inside them also.
|
||||||
|
```
|
23
test/unit/data/common/CodeBlocks.md
Normal file
23
test/unit/data/common/CodeBlocks.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# Code Blocks
|
||||||
|
|
||||||
|
This line won't *have any markdown* formatting applied.
|
||||||
|
I can even write <b>HTML</b> and it will show up as text.
|
||||||
|
This is great for showing program source code, or HTML or even
|
||||||
|
Markdown. <b>this won't show up as HTML</b> but
|
||||||
|
exactly <i>as you see it in this text file</i>.
|
||||||
|
|
||||||
|
Within a paragraph, you can use backquotes to do the same thing.
|
||||||
|
`This won't be *italic* or **bold** at all.`
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
int main(int argc, const char* argv[]) {
|
||||||
|
std::cout << "C++ code block test" << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
This is a code block without language identifier.
|
||||||
|
```
|
11
test/unit/data/common/Escapes.md
Normal file
11
test/unit/data/common/Escapes.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# Escapes
|
||||||
|
|
||||||
|
\# This isn't a heading
|
||||||
|
|
||||||
|
\*this isn't in italic\* and \_so is this\_
|
||||||
|
|
||||||
|
\*\*this isn't in bold\*\* and \_\_so is this\_\_
|
||||||
|
|
||||||
|
\`\`\`
|
||||||
|
This isn't a code block without language identifier.
|
||||||
|
\`\`\`
|
72
test/unit/data/common/Headings.md
Normal file
72
test/unit/data/common/Headings.md
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
# Headings
|
||||||
|
|
||||||
|
This is a huge header
|
||||||
|
===
|
||||||
|
|
||||||
|
this is a smaller header
|
||||||
|
---
|
||||||
|
|
||||||
|
header
|
||||||
|
---
|
||||||
|
|
||||||
|
## ATX Headings
|
||||||
|
|
||||||
|
# foo
|
||||||
|
|
||||||
|
bar
|
||||||
|
|
||||||
|
## foo
|
||||||
|
|
||||||
|
bar
|
||||||
|
|
||||||
|
### foo
|
||||||
|
|
||||||
|
bar
|
||||||
|
|
||||||
|
#### foo
|
||||||
|
|
||||||
|
bar
|
||||||
|
|
||||||
|
##### foo
|
||||||
|
|
||||||
|
bar
|
||||||
|
|
||||||
|
###### foo
|
||||||
|
|
||||||
|
bar
|
||||||
|
|
||||||
|
####### This isn't a heading
|
||||||
|
|
||||||
|
bar
|
||||||
|
|
||||||
|
#5 This isn't a heading
|
||||||
|
|
||||||
|
#This isn't a heading
|
||||||
|
|
||||||
|
## Horizontal Rule
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
foo
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
bar
|
||||||
|
|
||||||
|
## Failing Tests
|
||||||
|
|
||||||
|
Headings and horizontal rules are shrinked to three characters because this simplify the parsing - maybe we should change this.
|
||||||
|
|
||||||
|
```
|
||||||
|
This is a huge header
|
||||||
|
==================
|
||||||
|
|
||||||
|
this is a smaller header
|
||||||
|
------------------
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
## Horizontal Rule
|
||||||
|
|
||||||
|
----------------
|
||||||
|
```
|
3
test/unit/data/common/Images.md
Normal file
3
test/unit/data/common/Images.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Images
|
||||||
|
|
||||||
|

|
24
test/unit/data/common/Links.md
Normal file
24
test/unit/data/common/Links.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Links
|
||||||
|
|
||||||
|
[title](http://127.0.0.1)
|
||||||
|
|
||||||
|
[title with spaces](https://localhost)
|
||||||
|
|
||||||
|
- [title](http://127.0.0.1)
|
||||||
|
|
||||||
|
- [title with spaces](https://localhost)
|
||||||
|
|
||||||
|
- [ ] [title](http://127.0.0.1)
|
||||||
|
|
||||||
|
- [x] [title with spaces](https://localhost)
|
||||||
|
|
||||||
|
## Reference Links
|
||||||
|
|
||||||
|
You can also put the [link URL][1] below the current paragraph like [this][2].
|
||||||
|
|
||||||
|
[1]: http://url.local
|
||||||
|
[2]: http://another.url
|
||||||
|
|
||||||
|
Or you can use a [shortcut][] reference, which links the text "shortcut" to the link named "[shortcut]" on the next paragraph.
|
||||||
|
|
||||||
|
[shortcut]: http://goes/with/the/link/name/text
|
105
test/unit/data/common/Lists.md
Normal file
105
test/unit/data/common/Lists.md
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
# Lists
|
||||||
|
|
||||||
|
* an asterisk starts an unordered list
|
||||||
|
* and this is another item in the list
|
||||||
|
* and this is another item in the list
|
||||||
|
|
||||||
|
To start an ordered list, write this:
|
||||||
|
|
||||||
|
<!-- Strange indentation -->
|
||||||
|
|
||||||
|
1. this starts a list *with* numbers
|
||||||
|
2. this will show as number "2"
|
||||||
|
3. this will show as number "3."
|
||||||
|
4. any number, +, -, or * will keep the list going.
|
||||||
|
* just indent by 4 spaces (or tab) to make a sub-list
|
||||||
|
1. keep indenting for more sub lists
|
||||||
|
* here i'm back to the second level
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- foo
|
||||||
|
- bar
|
||||||
|
- baz
|
||||||
|
- boo
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- a
|
||||||
|
- b
|
||||||
|
- c
|
||||||
|
- d
|
||||||
|
- e
|
||||||
|
- f
|
||||||
|
- g
|
||||||
|
- h
|
||||||
|
- i
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- foo
|
||||||
|
-
|
||||||
|
- bar
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**TODO:** Empty comments should not be displayed as HTML in preview mode because they may be used to separate consecutive lists of the same type (CM Example 270).
|
||||||
|
|
||||||
|
- foo
|
||||||
|
- bar
|
||||||
|
|
||||||
|
<!-- -->
|
||||||
|
|
||||||
|
- baz
|
||||||
|
- bim
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
-one
|
||||||
|
|
||||||
|
2.two
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Failing Tests
|
||||||
|
|
||||||
|
```
|
||||||
|
* an asterisk starts an unordered list
|
||||||
|
* and this is another item in the list
|
||||||
|
+ or you can also use the + character
|
||||||
|
- or the - character
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
1. this starts a list *with* numbers
|
||||||
|
+ this will show as number "2"
|
||||||
|
* this will show as number "3."
|
||||||
|
9. any number, +, -, or * will keep the list going.
|
||||||
|
* just indent by 4 spaces (or tab) to make a sub-list
|
||||||
|
1. keep indenting for more sub lists
|
||||||
|
* here i'm back to the second level
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
1. this starts a list *with* numbers
|
||||||
|
+ this will show as number "2"
|
||||||
|
* this will show as number "3."
|
||||||
|
9. any number, +, -, or * will keep the list going.
|
||||||
|
* just indent by 2 spaces to make a sub-list
|
||||||
|
1. keep indenting for more sub lists
|
||||||
|
* here i'm back to the second level
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
CommonMark Example 266
|
||||||
|
|
||||||
|
Foo
|
||||||
|
- bar
|
||||||
|
- baz
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
- foo
|
||||||
|
-
|
||||||
|
- bar
|
||||||
|
```
|
3
test/unit/data/gfm/BasicTextFormatting.md
Normal file
3
test/unit/data/gfm/BasicTextFormatting.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Basic Text Formatting
|
||||||
|
|
||||||
|
~~this is strike through text~~
|
7
test/unit/data/gfm/Lists.md
Normal file
7
test/unit/data/gfm/Lists.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# GFM Lists
|
||||||
|
|
||||||
|
To start a check list, write this:
|
||||||
|
|
||||||
|
- [ ] this is not checked
|
||||||
|
- [ ] this is too
|
||||||
|
- [x] but this is checked
|
15
test/unit/data/gfm/Tables.md
Normal file
15
test/unit/data/gfm/Tables.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# Tables
|
||||||
|
|
||||||
|
| First Header | Second Header |
|
||||||
|
| -------------- | ---------------- |
|
||||||
|
| Co`nten`t Cell | Content Cell |
|
||||||
|
| Content Cell | **Content Cell** |
|
||||||
|
|
||||||
|
## Failing Tests
|
||||||
|
|
||||||
|
```
|
||||||
|
First Header | Second Header
|
||||||
|
------------ | -------------
|
||||||
|
Content Cell | Content Cell
|
||||||
|
Content Cell | Content Cell
|
||||||
|
```
|
@ -24,13 +24,15 @@ delete webpackConfig.entry
|
|||||||
delete webpackConfig.externals
|
delete webpackConfig.externals
|
||||||
delete webpackConfig.output.libraryTarget
|
delete webpackConfig.output.libraryTarget
|
||||||
|
|
||||||
// apply vue option to apply isparta-loader on js
|
// BUG: TypeError: Cannot read property 'loaders' of undefined
|
||||||
webpackConfig.module.rules
|
// // apply vue option to apply isparta-loader on js
|
||||||
.find(rule => rule.use.loader === 'vue-loader').use.options.loaders.js = 'babel-loader'
|
// webpackConfig.module.rules
|
||||||
|
// .find(rule => rule.use.loader === 'vue-loader').use.options.loaders.js = 'babel-loader'
|
||||||
|
|
||||||
module.exports = config => {
|
module.exports = config => {
|
||||||
config.set({
|
config.set({
|
||||||
browsers: ['visibleElectron'],
|
browsers: ['Electron'],
|
||||||
|
mode: 'development',
|
||||||
client: {
|
client: {
|
||||||
useIframe: false
|
useIframe: false
|
||||||
},
|
},
|
||||||
@ -41,12 +43,6 @@ module.exports = config => {
|
|||||||
{ type: 'text-summary' }
|
{ type: 'text-summary' }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
customLaunchers: {
|
|
||||||
'visibleElectron': {
|
|
||||||
base: 'Electron',
|
|
||||||
flags: ['--show']
|
|
||||||
}
|
|
||||||
},
|
|
||||||
frameworks: ['mocha', 'chai'],
|
frameworks: ['mocha', 'chai'],
|
||||||
files: ['./index.js'],
|
files: ['./index.js'],
|
||||||
preprocessors: {
|
preprocessors: {
|
||||||
|
54
test/unit/markdown.js
Normal file
54
test/unit/markdown.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import fs from 'fs'
|
||||||
|
import path from 'path'
|
||||||
|
|
||||||
|
const loadMarkdownContent = pathname => {
|
||||||
|
return fs.readFileSync(path.resolve('test/unit/data', pathname), 'utf-8')
|
||||||
|
}
|
||||||
|
|
||||||
|
export const BasicTextFormattingTemplate = () => {
|
||||||
|
return loadMarkdownContent('common/BasicTextFormatting.md')
|
||||||
|
}
|
||||||
|
|
||||||
|
export const BlockquotesTemplate= () => {
|
||||||
|
return loadMarkdownContent('common/Blockquotes.md')
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CodeBlocksTemplate = () => {
|
||||||
|
return loadMarkdownContent('common/CodeBlocks.md')
|
||||||
|
}
|
||||||
|
|
||||||
|
export const EscapesTemplate = () => {
|
||||||
|
return loadMarkdownContent('common/Escapes.md')
|
||||||
|
}
|
||||||
|
|
||||||
|
export const HeadingsTemplate = () => {
|
||||||
|
return loadMarkdownContent('common/Headings.md')
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ImagesTemplate = () => {
|
||||||
|
return loadMarkdownContent('common/Images.md')
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LinksTemplate = () => {
|
||||||
|
return loadMarkdownContent('common/Links.md')
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ListsTemplate = () => {
|
||||||
|
return loadMarkdownContent('common/Lists.md')
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------
|
||||||
|
// GFM templates
|
||||||
|
//
|
||||||
|
|
||||||
|
export const GfmBasicTextFormattingTemplate = () => {
|
||||||
|
return loadMarkdownContent('gfm/BasicTextFormatting.md')
|
||||||
|
}
|
||||||
|
|
||||||
|
export const GfmListsTemplate = () => {
|
||||||
|
return loadMarkdownContent('gfm/Lists.md')
|
||||||
|
}
|
||||||
|
|
||||||
|
export const GfmTablesTemplate = () => {
|
||||||
|
return loadMarkdownContent('gfm/Tables.md')
|
||||||
|
}
|
104
test/unit/specs/markdown-basic.spec.js
Normal file
104
test/unit/specs/markdown-basic.spec.js
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
import ContentState from '../../../src/muya/lib/contentState'
|
||||||
|
import EventCenter from '../../../src/muya/lib/eventHandler/event'
|
||||||
|
import ExportMarkdown from '../../../src/muya/lib/utils/exportMarkdown'
|
||||||
|
import { MUYA_DEFAULT_OPTION } from '../../../src/muya/lib/config'
|
||||||
|
import * as templates from '../markdown'
|
||||||
|
|
||||||
|
const defaultOptions = { 'endOfLine': 'lf' }
|
||||||
|
const defaultOptionsCrlf = Object.assign({}, defaultOptions, { 'endOfLine': 'crlf' })
|
||||||
|
|
||||||
|
const createMuyaContext = options => {
|
||||||
|
const ctx = {}
|
||||||
|
ctx.options = Object.assign({}, MUYA_DEFAULT_OPTION, options)
|
||||||
|
ctx.eventCenter = new EventCenter()
|
||||||
|
ctx.contentState = new ContentState(ctx, ctx.options)
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Muya parser (Markdown to HTML to Markdown)
|
||||||
|
//
|
||||||
|
|
||||||
|
const verifyMarkdown = (markdown, options) => {
|
||||||
|
const ctx = createMuyaContext(options)
|
||||||
|
ctx.contentState.importMarkdown(markdown)
|
||||||
|
|
||||||
|
const blocks = ctx.contentState.getBlocks()
|
||||||
|
const exportedMarkdown = new ExportMarkdown(blocks).generate()
|
||||||
|
|
||||||
|
// FIXME: We always need to add a new line at the end of the document. Add a option to disable the new line.
|
||||||
|
// Muya always use LF line endings.
|
||||||
|
expect(exportedMarkdown).to.equal(markdown)
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Muya parser', () => {
|
||||||
|
it('Basic Text Formatting', () => {
|
||||||
|
verifyMarkdown(templates.BasicTextFormattingTemplate(), defaultOptions)
|
||||||
|
})
|
||||||
|
it('Blockquotes', () => {
|
||||||
|
verifyMarkdown(templates.BlockquotesTemplate(), defaultOptions)
|
||||||
|
})
|
||||||
|
it('Code Blocks', () => {
|
||||||
|
verifyMarkdown(templates.CodeBlocksTemplate(), defaultOptions)
|
||||||
|
})
|
||||||
|
it('Escapes', () => {
|
||||||
|
verifyMarkdown(templates.EscapesTemplate(), defaultOptions)
|
||||||
|
})
|
||||||
|
it('Headings', () => {
|
||||||
|
verifyMarkdown(templates.HeadingsTemplate(), defaultOptions)
|
||||||
|
})
|
||||||
|
it('Images', () => {
|
||||||
|
verifyMarkdown(templates.ImagesTemplate(), defaultOptions)
|
||||||
|
})
|
||||||
|
it('Links', () => {
|
||||||
|
verifyMarkdown(templates.LinksTemplate(), defaultOptions)
|
||||||
|
})
|
||||||
|
it('Lists', () => {
|
||||||
|
verifyMarkdown(templates.ListsTemplate(), defaultOptions)
|
||||||
|
})
|
||||||
|
it('GFM - Basic Text Formatting', () => {
|
||||||
|
verifyMarkdown(templates.GfmBasicTextFormattingTemplate(), defaultOptions)
|
||||||
|
})
|
||||||
|
it('GFM - Lists', () => {
|
||||||
|
verifyMarkdown(templates.GfmListsTemplate(), defaultOptions)
|
||||||
|
})
|
||||||
|
it('GFM - Tables', () => {
|
||||||
|
verifyMarkdown(templates.GfmTablesTemplate(), defaultOptions)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Muya parser (CRLF)', () => {
|
||||||
|
it('Basic Text Formatting', () => {
|
||||||
|
verifyMarkdown(templates.BasicTextFormattingTemplate(), defaultOptionsCrlf)
|
||||||
|
})
|
||||||
|
it('Blockquotes', () => {
|
||||||
|
verifyMarkdown(templates.BlockquotesTemplate(), defaultOptionsCrlf)
|
||||||
|
})
|
||||||
|
it('Code Blocks', () => {
|
||||||
|
verifyMarkdown(templates.CodeBlocksTemplate(), defaultOptionsCrlf)
|
||||||
|
})
|
||||||
|
it('Escapes', () => {
|
||||||
|
verifyMarkdown(templates.EscapesTemplate(), defaultOptionsCrlf)
|
||||||
|
})
|
||||||
|
it('Headings', () => {
|
||||||
|
verifyMarkdown(templates.HeadingsTemplate(), defaultOptionsCrlf)
|
||||||
|
})
|
||||||
|
it('Images', () => {
|
||||||
|
verifyMarkdown(templates.ImagesTemplate(), defaultOptionsCrlf)
|
||||||
|
})
|
||||||
|
it('Links', () => {
|
||||||
|
verifyMarkdown(templates.LinksTemplate(), defaultOptionsCrlf)
|
||||||
|
})
|
||||||
|
it('Lists', () => {
|
||||||
|
verifyMarkdown(templates.ListsTemplate(), defaultOptionsCrlf)
|
||||||
|
})
|
||||||
|
it('GFM - Basic Text Formatting', () => {
|
||||||
|
verifyMarkdown(templates.GfmBasicTextFormattingTemplate(), defaultOptionsCrlf)
|
||||||
|
})
|
||||||
|
it('GFM - Lists', () => {
|
||||||
|
verifyMarkdown(templates.GfmListsTemplate(), defaultOptionsCrlf)
|
||||||
|
})
|
||||||
|
it('GFM - Tables', () => {
|
||||||
|
verifyMarkdown(templates.GfmTablesTemplate(), defaultOptionsCrlf)
|
||||||
|
})
|
||||||
|
})
|
Loading…
Reference in New Issue
Block a user