From d37caeeded0ebe90a2e922205eed94b5cc4fcca4 Mon Sep 17 00:00:00 2001 From: taehoon Date: Tue, 3 Dec 2019 14:40:51 -0500 Subject: add html-webpack-plugin to karma config --- test/unit/karma.conf.js | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'test') diff --git a/test/unit/karma.conf.js b/test/unit/karma.conf.js index 8af05c24..45d74f14 100644 --- a/test/unit/karma.conf.js +++ b/test/unit/karma.conf.js @@ -5,6 +5,7 @@ // var path = require('path') var merge = require('webpack-merge') +var HtmlWebpackPlugin = require('html-webpack-plugin') var baseConfig = require('../../build/webpack.base.conf') var utils = require('../../build/utils') var webpack = require('webpack') @@ -24,6 +25,11 @@ var webpackConfig = merge(baseConfig, { plugins: [ new webpack.DefinePlugin({ 'process.env': require('../../config/test.env') + }), + new HtmlWebpackPlugin({ + filename: 'index.html', + template: 'index.html', + inject: true }) ] }) -- cgit v1.2.3-70-g09d2 From cb92865dac3c8d961be01d506726b1692ce50d03 Mon Sep 17 00:00:00 2001 From: seven Date: Wed, 11 Dec 2019 14:22:11 +0500 Subject: upgrade babel-register --- package.json | 2 +- test/e2e/nightwatch.conf.js | 2 +- yarn.lock | 35 +++++++++++++++++++++++++++++++++-- 3 files changed, 35 insertions(+), 4 deletions(-) (limited to 'test') diff --git a/package.json b/package.json index c1da6ded..38936b23 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "@babel/core": "^7.7.5", "@babel/plugin-transform-runtime": "^7.7.6", "@babel/preset-env": "^7.7.6", + "@babel/register": "^7.7.4", "@vue/babel-helper-vue-jsx-merge-props": "^1.0.0", "@vue/babel-plugin-transform-vue-jsx": "^1.1.2", "@vue/test-utils": "^1.0.0-beta.26", @@ -49,7 +50,6 @@ "babel-eslint": "^7.0.0", "babel-loader": "^8.0.6", "babel-plugin-lodash": "^3.3.4", - "babel-register": "^6.0.0", "chai": "^3.5.0", "chalk": "^1.1.3", "chromedriver": "^2.21.2", diff --git a/test/e2e/nightwatch.conf.js b/test/e2e/nightwatch.conf.js index 2fc3af0b..07d974df 100644 --- a/test/e2e/nightwatch.conf.js +++ b/test/e2e/nightwatch.conf.js @@ -1,4 +1,4 @@ -require('babel-register') +require('@babel/register') var config = require('../../config') // http://nightwatchjs.org/guide#settings-file diff --git a/yarn.lock b/yarn.lock index 99ddd096..4b20a6a1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -645,6 +645,17 @@ js-levenshtein "^1.1.3" semver "^5.5.0" +"@babel/register@^7.7.4": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.7.4.tgz#45a4956471a9df3b012b747f5781cc084ee8f128" + integrity sha512-/fmONZqL6ZMl9KJUYajetCrID6m0xmL4odX7v+Xvoxcv0DdbP/oO0TWIeLUCHqczQ6L6njDMqmqHFy2cp3FFsA== + dependencies: + find-cache-dir "^2.0.0" + lodash "^4.17.13" + make-dir "^2.1.0" + pirates "^4.0.0" + source-map-support "^0.5.16" + "@babel/runtime@^7.7.6": version "7.7.6" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.7.6.tgz#d18c511121aff1b4f2cd1d452f1bac9601dd830f" @@ -1252,7 +1263,7 @@ babel-plugin-lodash@^3.3.4: lodash "^4.17.10" require-package-name "^2.0.1" -babel-register@^6.0.0, babel-register@^6.26.0: +babel-register@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071" dependencies: @@ -4950,7 +4961,7 @@ lru-cache@~2.6.5: version "2.6.5" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.6.5.tgz#e56d6354148ede8d7707b58d143220fd08df0fd5" -make-dir@^2.0.0: +make-dir@^2.0.0, make-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" dependencies: @@ -5368,6 +5379,11 @@ node-libs-browser@^2.0.0: util "^0.11.0" vm-browserify "0.0.4" +node-modules-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" + integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= + node-pre-gyp@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz#39ba4bb1439da030295f899e3b520b7785766149" @@ -5885,6 +5901,13 @@ pinkie@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" +pirates@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" + integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA== + dependencies: + node-modules-regexp "^1.0.0" + pkg-dir@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4" @@ -7130,6 +7153,14 @@ source-map-support@^0.4.15: dependencies: source-map "^0.5.6" +source-map-support@^0.5.16: + version "0.5.16" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" + integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + source-map-support@~0.5.10: version "0.5.12" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.12.tgz#b4f3b10d51857a5af0138d3ce8003b201613d599" -- cgit v1.2.3-70-g09d2 From 622c9d388e0df1f53c544c34b7def2bb6fe498cd Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Sun, 12 Jan 2020 03:44:06 +0200 Subject: Refactoring, forgotten files --- src/components/color_input/color_input.scss | 65 +++ src/services/color_convert/color_convert.js | 99 ++++- src/services/style_setter/style_setter.js | 442 ++------------------- src/services/theme_data/theme_data.service.js | 315 +++++++++++++++ .../services/style_setter/style_setter.spec.js | 79 ++++ 5 files changed, 578 insertions(+), 422 deletions(-) create mode 100644 src/components/color_input/color_input.scss create mode 100644 src/services/theme_data/theme_data.service.js create mode 100644 test/unit/specs/services/style_setter/style_setter.spec.js (limited to 'test') diff --git a/src/components/color_input/color_input.scss b/src/components/color_input/color_input.scss new file mode 100644 index 00000000..92bf87c5 --- /dev/null +++ b/src/components/color_input/color_input.scss @@ -0,0 +1,65 @@ +@import '../../_variables.scss'; + +.color-input { + display: inline-flex; + + &-field.input { + display: inline-flex; + flex: 0 0 0; + max-width: 9em; + align-items: stretch; + padding: .2em 8px; + + input { + background: none; + color: $fallback--lightText; + color: var(--inputText, $fallback--lightText); + border: none; + padding: 0; + margin: 0; + + &.textColor { + flex: 1 0 3em; + min-width: 3em; + padding: 0; + } + + &.nativeColor { + flex: 0 0 2em; + min-width: 2em; + align-self: center; + height: 100%; + } + } + .transparentIndicator { + flex: 0 0 2em; + min-width: 2em; + align-self: center; + height: 100%; + // forgot to install counter-strike source, ooops + background-color: #FF00FF; + position: relative; + &::before, &::after { + display: block; + content: ''; + background-color: #000000; + position: absolute; + height: 50%; + width: 50%; + } + &::after { + top: 0; + left: 0; + } + &::before { + bottom: 0; + right: 0; + } + } + } + + .label { + flex: 1 1 auto; + } + +} diff --git a/src/services/color_convert/color_convert.js b/src/services/color_convert/color_convert.js index 32b4d50e..464f6495 100644 --- a/src/services/color_convert/color_convert.js +++ b/src/services/color_convert/color_convert.js @@ -1,9 +1,16 @@ -import { map } from 'lodash' +import { invertLightness, convert, contrastRatio } from 'chromatism' // useful for visualizing color when debugging export const consoleColor = (color) => console.log('%c##########', 'background: ' + color + '; color: ' + color) -const rgb2hex = (r, g, b) => { +/** + * Convert r, g, b values into hex notation. All components are [0-255] + * + * @param {Number|String|Object} r - Either red component, {r,g,b} object, or hex string + * @param {Number} [g] - Green component + * @param {Number} [b] - Blue component + */ +export const rgb2hex = (r, g, b) => { if (r === null || typeof r === 'undefined') { return undefined } @@ -14,7 +21,7 @@ const rgb2hex = (r, g, b) => { if (typeof r === 'object') { ({ r, g, b } = r) } - [r, g, b] = map([r, g, b], (val) => { + [r, g, b] = [r, g, b].map(val => { val = Math.ceil(val) val = val < 0 ? 0 : val val = val > 255 ? 255 : val @@ -82,6 +89,7 @@ const getContrastRatio = (a, b) => { return (l1 + 0.05) / (l2 + 0.05) } + /** * Same as `getContrastRatio` but for multiple layers in-between * @@ -101,7 +109,7 @@ export const getContrastRatioLayers = (text, layers, bedrock) => { * @param {Object} bg - bottom layer color * @returns {Object} sRGB of resulting color */ -const alphaBlend = (fg, fga, bg) => { +export const alphaBlend = (fg, fga, bg) => { if (fga === 1 || typeof fga === 'undefined') return fg return 'rgb'.split('').reduce((acc, c) => { // Simplified https://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending @@ -121,14 +129,20 @@ export const alphaBlendLayers = (bedrock, layers) => layers.reduce((acc, [color, return alphaBlend(color, opacity, acc) }, bedrock) -const invert = (rgb) => { +export const invert = (rgb) => { return 'rgb'.split('').reduce((acc, c) => { acc[c] = 255 - rgb[c] return acc }, {}) } -const hex2rgb = (hex) => { +/** + * Converts #rrggbb hex notation into an {r, g, b} object + * + * @param {String} hex - #rrggbb string + * @returns {Object} rgb representation of the color, values are 0-255 + */ +export const hex2rgb = (hex) => { const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex) return result ? { r: parseInt(result[1], 16), @@ -137,18 +151,75 @@ const hex2rgb = (hex) => { } : null } -const mixrgb = (a, b) => { +/** + * Old somewhat weird function for mixing two colors together + * + * @param {Object} a - one color (rgb) + * @param {Object} b - other color (rgb) + * @returns {Object} result + */ +export const mixrgb = (a, b) => { return Object.keys(a).reduce((acc, k) => { acc[k] = (a[k] + b[k]) / 2 return acc }, {}) } +/** + * Converts rgb object into a CSS rgba() color + * + * @param {Object} color - rgb + * @returns {String} CSS rgba() color + */ +export const rgba2css = function (rgba) { + return `rgba(${rgba.r}, ${rgba.g}, ${rgba.b}, ${rgba.a})` +} + +/** + * Get text color for given background color and intended text color + * This checks if text and background don't have enough color and inverts + * text color's lightness if needed. If text color is still not enough it + * will fall back to black or white + * + * @param {Object} bg - background color + * @param {Object} text - intended text color + * @param {Boolean} preserve - try to preserve intended text color's hue/saturation (i.e. no BW) + */ +export const getTextColor = function (bg, text, preserve) { + const bgIsLight = convert(bg).hsl.l > 50 + const textIsLight = convert(text).hsl.l > 50 + + if ((bgIsLight && textIsLight) || (!bgIsLight && !textIsLight)) { + const base = typeof text.a !== 'undefined' ? { a: text.a } : {} + const result = Object.assign(base, invertLightness(text).rgb) + if (!preserve && getContrastRatio(bg, result) < 4.5) { + // B&W + return contrastRatio(bg, text).rgb + } + // Inverted color + return result + } + return text +} -export { - rgb2hex, - hex2rgb, - mixrgb, - invert, - getContrastRatio, - alphaBlend +/** + * Converts color to CSS Color value + * + * @param {Object|String} input - color + * @param {Number} [a] - alpha value + * @returns {String} a CSS Color value + */ +export const getCssColor = (input, a) => { + let rgb = {} + if (typeof input === 'object') { + rgb = input + } else if (typeof input === 'string') { + if (input.startsWith('#')) { + rgb = hex2rgb(input) + } else if (input.startsWith('--')) { + return `var(${input})` + } else { + return input + } + } + return rgba2css({ ...rgb, a }) } diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js index 992b3194..46b08628 100644 --- a/src/services/style_setter/style_setter.js +++ b/src/services/style_setter/style_setter.js @@ -1,275 +1,13 @@ import { times } from 'lodash' -import { brightness, invertLightness, convert, contrastRatio } from 'chromatism' -import { rgb2hex, hex2rgb, mixrgb, getContrastRatio, alphaBlend, alphaBlendLayers } from '../color_convert/color_convert.js' - -export const CURRENT_VERSION = 3 -/* This is a definition of all layer combinations - * each key is a topmost layer, each value represents layer underneath - * this is essentially a simplified tree - */ -export const LAYERS = { - undelay: null, // root - topBar: null, // no transparency support - badge: null, // no transparency support - fg: null, - bg: 'underlay', - panel: 'bg', - btn: 'bg', - btnPanel: 'panel', - btnTopBar: 'topBar', - input: 'bg', - inputPanel: 'panel', - inputTopBar: 'topBar', - alert: 'bg', - alertPanel: 'panel' -} - -export const SLOT_INHERITANCE = { - bg: null, - fg: null, - text: null, - underlay: '#000000', - link: '--accent', - accent: '--link', - faint: '--text', - faintLink: '--link', - - cBlue: '#0000ff', - cRed: '#FF0000', - cGreen: '#00FF00', - cOrange: '#E3FF00', - - lightBg: { - depends: ['bg'], - color: (mod, bg) => brightness(5 * mod, bg).rgb - }, - lightText: { - depends: ['text'], - color: (mod, text) => brightness(20 * mod, text).rgb - }, - - border: { - depends: 'fg', - color: (mod, fg) => brightness(2 * mod, fg).rgb - }, - - linkBg: { - depends: ['accent', 'bg'], - color: (mod, accent, bg) => alphaBlend(accent, 0.4, bg).rgb - }, - - icon: { - depends: ['bg', 'text'], - color: (mod, bg, text) => mixrgb(bg, text) - }, - - // Foreground - fgText: { - depends: ['text'], - layer: 'fg', - textColor: true - }, - fgLink: { - depends: ['link'], - layer: 'fg', - textColor: 'preserve' - }, - - // Panel header - panel: '--fg', - panelText: { - depends: ['fgText'], - layer: 'panel', - textColor: true - }, - panelFaint: { - depends: ['fgText'], - layer: 'panel', - textColor: true - }, - panelLink: { - depends: ['fgLink'], - layer: 'panel', - textColor: 'preserve' - }, - - // Top bar - topBar: '--fg', - topBarText: { - depends: ['fgText'], - layer: 'topBar', - textColor: true - }, - topBarLink: { - depends: ['fgLink'], - layer: 'topBar', - textColor: 'preserve' - }, - - // Buttons - btn: '--fg', - btnText: { - depends: ['fgText'], - layer: 'btn' - }, - btnPanelText: { - depends: ['panelText'], - layer: 'btnPanel', - variant: 'btn', - textColor: true - }, - btnTopBarText: { - depends: ['topBarText'], - layer: 'btnTopBar', - variant: 'btn', - textColor: true - }, - - // Input fields - input: '--fg', - inputText: { - depends: ['text'], - layer: 'input', - textColor: true - }, - inputPanelText: { - depends: ['panelText'], - layer: 'inputPanel', - variant: 'input', - textColor: true - }, - inputTopbarText: { - depends: ['topBarText'], - layer: 'inputTopBar', - variant: 'input', - textColor: true - }, - - alertError: '--cRed', - alertErrorText: { - depends: ['text', 'alertError'], - layer: 'alert', - variant: 'alertError', - textColor: true - }, - alertErrorPanelText: { - depends: ['panelText', 'alertError'], - layer: 'alertPanel', - variant: 'alertError', - textColor: true - }, - - alertWarning: '--cOrange', - alertWarningText: { - depends: ['text', 'alertWarning'], - layer: 'alert', - variant: 'alertWarning', - textColor: true - }, - alertWarningPanelText: { - depends: ['panelText', 'alertWarning'], - layer: 'alertPanel', - variant: 'alertWarning', - textColor: true - }, - - badgeNotification: '--cRed', - badgeNotificationText: { - depends: ['text', 'badgeNotification'], - layer: 'badge', - variant: 'badgeNotification', - textColor: 'bw' - } -} - -export const getLayersArray = (layer, data = LAYERS) => { - let array = [layer] - let parent = data[layer] - while (parent) { - array.unshift(parent) - parent = data[parent] - } - return array -} - -export const getLayers = (layer, variant = layer, colors, opacity) => { - return getLayersArray(layer).map((currentLayer) => ([ - currentLayer === layer - ? colors[variant] - : colors[currentLayer], - opacity[currentLayer] - ])) -} - -const getDependencies = (key, inheritance) => { - const data = inheritance[key] - if (typeof data === 'string' && data.startsWith('--')) { - return [data.substring(2)] - } else { - if (data === null) return [] - const { depends, layer, variant } = data - const layerDeps = layer - ? getLayersArray(layer).map(currentLayer => { - return currentLayer === layer - ? variant || layer - : currentLayer - }) - : [] - if (Array.isArray(depends)) { - return [...depends, ...layerDeps] - } else { - return [...layerDeps] - } - } -} - -export const topoSort = ( - inheritance = SLOT_INHERITANCE, - getDeps = getDependencies -) => { - // This is an implementation of https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm - - const allKeys = Object.keys(inheritance) - const whites = new Set(allKeys) - const grays = new Set() - const blacks = new Set() - const unprocessed = [...allKeys] - const output = [] - - const step = (node) => { - if (whites.has(node)) { - // Make node "gray" - whites.delete(node) - grays.add(node) - // Do step for each node connected to it (one way) - getDeps(node, inheritance).forEach(step) - // Make node "black" - grays.delete(node) - blacks.add(node) - // Put it into the output list - output.push(node) - } else if (grays.has(node)) { - console.debug('Cyclic depenency in topoSort, ignoring') - output.push(node) - } else if (blacks.has(node)) { - // do nothing - } else { - throw new Error('Unintended condition in topoSort!') - } - } - while (unprocessed.length > 0) { - step(unprocessed.pop()) - } - return output -} - -export const SLOT_ORDERED = topoSort(SLOT_INHERITANCE) +import { convert } from 'chromatism' +import { rgb2hex, hex2rgb, rgba2css, getCssColor } from '../color_convert/color_convert.js' +import { getColors } from '../theme_data/theme_data.service.js' // While this is not used anymore right now, I left it in if we want to do custom // styles that aren't just colors, so user can pick from a few different distinct // styles as well as set their own colors in the future. -const setStyle = (href, commit) => { +export const setStyle = (href, commit) => { /*** What's going on here? I want to make it easy for admins to style this application. To have @@ -315,30 +53,7 @@ const setStyle = (href, commit) => { cssEl.addEventListener('load', setDynamic) } -const rgb2rgba = function (rgba) { - return `rgba(${rgba.r}, ${rgba.g}, ${rgba.b}, ${rgba.a})` -} - -const getTextColor = function (bg, text, preserve) { - const bgIsLight = convert(bg).hsl.l > 50 - const textIsLight = convert(text).hsl.l > 50 - - console.log(bgIsLight, textIsLight) - - if ((bgIsLight && textIsLight) || (!bgIsLight && !textIsLight)) { - const base = typeof text.a !== 'undefined' ? { a: text.a } : {} - const result = Object.assign(base, invertLightness(text).rgb) - if (!preserve && getContrastRatio(bg, result) < 4.5) { - // B&W - return contrastRatio(bg, text).rgb - } - // Inverted color - return result - } - return text -} - -const applyTheme = (input, commit) => { +export const applyTheme = (input, commit) => { const { rules, theme } = generatePreset(input) const head = document.head const body = document.body @@ -399,22 +114,6 @@ const getCssShadowFilter = (input) => { .join(' ') } -const getCssColor = (input, a) => { - let rgb = {} - if (typeof input === 'object') { - rgb = input - } else if (typeof input === 'string') { - if (input.startsWith('#')) { - rgb = hex2rgb(input) - } else if (input.startsWith('--')) { - return `var(${input})` - } else { - return input - } - } - return rgb2rgba({ ...rgb, a }) -} - const generateColors = (themeData) => { const rawOpacity = Object.assign({ panel: 1, @@ -435,14 +134,16 @@ const generateColors = (themeData) => { }, {})) const inputColors = themeData.colors || themeData - const transparentsOpacity = Object.entries(inputColors).reduce((acc, [k, v]) => { - if (v === 'transparent') { - acc[k] = 0 - } - return acc - }, {}) - const opacity = { ...rawOpacity, ...transparentsOpacity } + const opacity = { + ...rawOpacity, + ...Object.entries(inputColors).reduce((acc, [k, v]) => { + if (v === 'transparent') { + acc[k] = 0 + } + return acc + }, {}) + } // Cycle one: just whatever we have const sourceColors = Object.entries(inputColors).reduce((acc, [k, v]) => { @@ -462,55 +163,7 @@ const generateColors = (themeData) => { const isLightOnDark = convert(sourceColors.bg).hsl.l < convert(sourceColors.text).hsl.l const mod = isLightOnDark ? 1 : -1 - const colors = SLOT_ORDERED.reduce((acc, key) => { - const value = SLOT_INHERITANCE[key] - if (sourceColors[key]) { - return { ...acc, [key]: { ...sourceColors[key] } } - } else if (typeof value === 'string' && value.startsWith('#')) { - return { ...acc, [key]: convert(value).rgb } - } else { - const isObject = typeof value === 'object' - const defaultColorFunc = (mod, dep) => ({ ...dep }) - const deps = getDependencies(key, SLOT_INHERITANCE) - const colorFunc = (isObject && value.color) || defaultColorFunc - - if (value.textColor) { - const bg = alphaBlendLayers( - { ...acc[deps[0]] }, - getLayers( - value.layer, - value.variant || value.layer, - acc, - opacity - ) - ) - if (value.textColor === 'bw') { - return { - ...acc, - [key]: contrastRatio(bg) - } - } else { - return { - ...acc, - [key]: getTextColor( - bg, - { ...acc[deps[0]] }, - value.textColor === 'preserve' - ) - } - } - } else { - console.log('BENIS', key, deps, deps.map((dep) => ({ ...acc[dep] }))) - return { - ...acc, - [key]: colorFunc( - mod, - ...deps.map((dep) => ({ ...acc[dep] })) - ) - } - } - } - }, {}) + const colors = getColors(sourceColors, opacity, mod) // Inheriting opacities Object.entries(opacity).forEach(([ k, v ]) => { @@ -541,7 +194,7 @@ const generateColors = (themeData) => { .reduce((acc, [k, v]) => { if (!v) return acc acc.solid[k] = rgb2hex(v) - acc.complete[k] = typeof v.a === 'undefined' ? rgb2hex(v) : rgb2rgba(v) + acc.complete[k] = typeof v.a === 'undefined' ? rgb2hex(v) : rgba2css(v) return acc }, { complete: {}, solid: {} }) return { @@ -740,14 +393,12 @@ const composePreset = (colors, radii, shadows, fonts) => { } } -const generatePreset = (input) => { - const shadows = generateShadows(input) - const colors = generateColors(input) - const radii = generateRadii(input) - const fonts = generateFonts(input) - - return composePreset(colors, radii, shadows, fonts) -} +const generatePreset = (input) => composePreset( + generateColors(input), + generateRadii(input), + generateShadows(input), + generateFonts(input) +) const getThemes = () => { return window.fetch('/static/styles.json') @@ -779,33 +430,24 @@ const getThemes = () => { }) } -const setPreset = (val, commit) => { +export const setPreset = (val, commit) => { return getThemes().then((themes) => { const theme = themes[val] ? themes[val] : themes['pleroma-dark'] const isV1 = Array.isArray(theme) const data = isV1 ? {} : theme.theme if (isV1) { - const bgRgb = hex2rgb(theme[1]) - const fgRgb = hex2rgb(theme[2]) - const textRgb = hex2rgb(theme[3]) - const linkRgb = hex2rgb(theme[4]) - - const cRedRgb = hex2rgb(theme[5] || '#FF0000') - const cGreenRgb = hex2rgb(theme[6] || '#00FF00') - const cBlueRgb = hex2rgb(theme[7] || '#0000FF') - const cOrangeRgb = hex2rgb(theme[8] || '#E3FF00') - - data.colors = { - bg: bgRgb, - fg: fgRgb, - text: textRgb, - link: linkRgb, - cRed: cRedRgb, - cBlue: cBlueRgb, - cGreen: cGreenRgb, - cOrange: cOrangeRgb - } + const bg = hex2rgb(theme[1]) + const fg = hex2rgb(theme[2]) + const text = hex2rgb(theme[3]) + const link = hex2rgb(theme[4]) + + const cRed = hex2rgb(theme[5] || '#FF0000') + const cGreen = hex2rgb(theme[6] || '#00FF00') + const cBlue = hex2rgb(theme[7] || '#0000FF') + const cOrange = hex2rgb(theme[8] || '#E3FF00') + + data.colors = { bg, fg, text, link, cRed, cBlue, cGreen, cOrange } } // This is a hack, this function is only called during initial load. @@ -819,19 +461,3 @@ const setPreset = (val, commit) => { } }) } - -export { - setStyle, - setPreset, - applyTheme, - getTextColor, - generateColors, - generateRadii, - generateShadows, - generateFonts, - generatePreset, - getThemes, - composePreset, - getCssShadow, - getCssShadowFilter -} diff --git a/src/services/theme_data/theme_data.service.js b/src/services/theme_data/theme_data.service.js new file mode 100644 index 00000000..c9c80727 --- /dev/null +++ b/src/services/theme_data/theme_data.service.js @@ -0,0 +1,315 @@ +import { convert, brightness, contrastRatio } from 'chromatism' +import { alphaBlend, alphaBlendLayers, getTextColor, mixrgb } from '../color_convert/color_convert.js' + +export const CURRENT_VERSION = 3 +/* This is a definition of all layer combinations + * each key is a topmost layer, each value represents layer underneath + * this is essentially a simplified tree + */ +export const LAYERS = { + undelay: null, // root + topBar: null, // no transparency support + badge: null, // no transparency support + fg: null, + bg: 'underlay', + panel: 'bg', + btn: 'bg', + btnPanel: 'panel', + btnTopBar: 'topBar', + input: 'bg', + inputPanel: 'panel', + inputTopBar: 'topBar', + alert: 'bg', + alertPanel: 'panel' +} + +export const SLOT_INHERITANCE = { + bg: null, + fg: null, + text: null, + underlay: '#000000', + link: '--accent', + accent: '--link', + faint: '--text', + faintLink: '--link', + + cBlue: '#0000ff', + cRed: '#FF0000', + cGreen: '#00FF00', + cOrange: '#E3FF00', + + lightBg: { + depends: ['bg'], + color: (mod, bg) => brightness(5 * mod, bg).rgb + }, + lightText: { + depends: ['text'], + color: (mod, text) => brightness(20 * mod, text).rgb + }, + + border: { + depends: 'fg', + color: (mod, fg) => brightness(2 * mod, fg).rgb + }, + + linkBg: { + depends: ['accent', 'bg'], + color: (mod, accent, bg) => alphaBlend(accent, 0.4, bg).rgb + }, + + icon: { + depends: ['bg', 'text'], + color: (mod, bg, text) => mixrgb(bg, text) + }, + + // Foreground + fgText: { + depends: ['text'], + layer: 'fg', + textColor: true + }, + fgLink: { + depends: ['link'], + layer: 'fg', + textColor: 'preserve' + }, + + // Panel header + panel: '--fg', + panelText: { + depends: ['fgText'], + layer: 'panel', + textColor: true + }, + panelFaint: { + depends: ['fgText'], + layer: 'panel', + textColor: true + }, + panelLink: { + depends: ['fgLink'], + layer: 'panel', + textColor: 'preserve' + }, + + // Top bar + topBar: '--fg', + topBarText: { + depends: ['fgText'], + layer: 'topBar', + textColor: true + }, + topBarLink: { + depends: ['fgLink'], + layer: 'topBar', + textColor: 'preserve' + }, + + // Buttons + btn: '--fg', + btnText: { + depends: ['fgText'], + layer: 'btn' + }, + btnPanelText: { + depends: ['panelText'], + layer: 'btnPanel', + variant: 'btn', + textColor: true + }, + btnTopBarText: { + depends: ['topBarText'], + layer: 'btnTopBar', + variant: 'btn', + textColor: true + }, + + // Input fields + input: '--fg', + inputText: { + depends: ['text'], + layer: 'input', + textColor: true + }, + inputPanelText: { + depends: ['panelText'], + layer: 'inputPanel', + variant: 'input', + textColor: true + }, + inputTopbarText: { + depends: ['topBarText'], + layer: 'inputTopBar', + variant: 'input', + textColor: true + }, + + alertError: '--cRed', + alertErrorText: { + depends: ['text', 'alertError'], + layer: 'alert', + variant: 'alertError', + textColor: true + }, + alertErrorPanelText: { + depends: ['panelText', 'alertError'], + layer: 'alertPanel', + variant: 'alertError', + textColor: true + }, + + alertWarning: '--cOrange', + alertWarningText: { + depends: ['text', 'alertWarning'], + layer: 'alert', + variant: 'alertWarning', + textColor: true + }, + alertWarningPanelText: { + depends: ['panelText', 'alertWarning'], + layer: 'alertPanel', + variant: 'alertWarning', + textColor: true + }, + + badgeNotification: '--cRed', + badgeNotificationText: { + depends: ['text', 'badgeNotification'], + layer: 'badge', + variant: 'badgeNotification', + textColor: 'bw' + } +} + +export const getLayersArray = (layer, data = LAYERS) => { + let array = [layer] + let parent = data[layer] + while (parent) { + array.unshift(parent) + parent = data[parent] + } + return array +} + +export const getLayers = (layer, variant = layer, colors, opacity) => { + return getLayersArray(layer).map((currentLayer) => ([ + currentLayer === layer + ? colors[variant] + : colors[currentLayer], + opacity[currentLayer] + ])) +} + +const getDependencies = (key, inheritance) => { + const data = inheritance[key] + if (typeof data === 'string' && data.startsWith('--')) { + return [data.substring(2)] + } else { + if (data === null) return [] + const { depends, layer, variant } = data + const layerDeps = layer + ? getLayersArray(layer).map(currentLayer => { + return currentLayer === layer + ? variant || layer + : currentLayer + }) + : [] + if (Array.isArray(depends)) { + return [...depends, ...layerDeps] + } else { + return [...layerDeps] + } + } +} + +export const topoSort = ( + inheritance = SLOT_INHERITANCE, + getDeps = getDependencies +) => { + // This is an implementation of https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm + + const allKeys = Object.keys(inheritance) + const whites = new Set(allKeys) + const grays = new Set() + const blacks = new Set() + const unprocessed = [...allKeys] + const output = [] + + const step = (node) => { + if (whites.has(node)) { + // Make node "gray" + whites.delete(node) + grays.add(node) + // Do step for each node connected to it (one way) + getDeps(node, inheritance).forEach(step) + // Make node "black" + grays.delete(node) + blacks.add(node) + // Put it into the output list + output.push(node) + } else if (grays.has(node)) { + console.debug('Cyclic depenency in topoSort, ignoring') + output.push(node) + } else if (blacks.has(node)) { + // do nothing + } else { + throw new Error('Unintended condition in topoSort!') + } + } + while (unprocessed.length > 0) { + step(unprocessed.pop()) + } + return output +} + +export const SLOT_ORDERED = topoSort(SLOT_INHERITANCE) + +export const getColors = (sourceColors, sourceOpacity, mod) => SLOT_ORDERED.reduce((acc, key) => { + const value = SLOT_INHERITANCE[key] + if (sourceColors[key]) { + return { ...acc, [key]: { ...sourceColors[key] } } + } else if (typeof value === 'string' && value.startsWith('#')) { + return { ...acc, [key]: convert(value).rgb } + } else { + const isObject = typeof value === 'object' + const defaultColorFunc = (mod, dep) => ({ ...dep }) + const deps = getDependencies(key, SLOT_INHERITANCE) + const colorFunc = (isObject && value.color) || defaultColorFunc + + if (value.textColor) { + const bg = alphaBlendLayers( + { ...acc[deps[0]] }, + getLayers( + value.layer, + value.variant || value.layer, + acc, + sourceOpacity + ) + ) + if (value.textColor === 'bw') { + return { + ...acc, + [key]: contrastRatio(bg) + } + } else { + return { + ...acc, + [key]: getTextColor( + bg, + { ...acc[deps[0]] }, + value.textColor === 'preserve' + ) + } + } + } else { + console.log('BENIS', key, deps, deps.map((dep) => ({ ...acc[dep] }))) + return { + ...acc, + [key]: colorFunc( + mod, + ...deps.map((dep) => ({ ...acc[dep] })) + ) + } + } + } +}, {}) diff --git a/test/unit/specs/services/style_setter/style_setter.spec.js b/test/unit/specs/services/style_setter/style_setter.spec.js new file mode 100644 index 00000000..7f789124 --- /dev/null +++ b/test/unit/specs/services/style_setter/style_setter.spec.js @@ -0,0 +1,79 @@ +import { getLayersArray, topoSort } from 'src/services/style_setter/style_setter' + +describe('getLayersArray', () => { + const fixture = { + layer1: null, + layer2: 'layer1', + layer3a: 'layer2', + layer3b: 'layer2' + } + + it('should expand layers properly (3b)', () => { + const out = getLayersArray('layer3b', fixture) + expect(out).to.eql(['layer1', 'layer2', 'layer3b']) + }) + + it('should expand layers properly (3a)', () => { + const out = getLayersArray('layer3a', fixture) + expect(out).to.eql(['layer1', 'layer2', 'layer3a']) + }) + + it('should expand layers properly (2)', () => { + const out = getLayersArray('layer2', fixture) + expect(out).to.eql(['layer1', 'layer2']) + }) + + it('should expand layers properly (1)', () => { + const out = getLayersArray('layer1', fixture) + expect(out).to.eql(['layer1']) + }) +}) + +describe('topoSort', () => { + const fixture1 = { + layerA: [], + layer1A: ['layerA'], + layer2A: ['layer1A'], + layerB: [], + layer1B: ['layerB'], + layer2B: ['layer1B'], + layer3AB: ['layer2B', 'layer2A'] + } + + // Same thing but messed up order + const fixture2 = { + layer1A: ['layerA'], + layer1B: ['layerB'], + layer2A: ['layer1A'], + layerB: [], + layer3AB: ['layer2B', 'layer2A'], + layer2B: ['layer1B'], + layerA: [] + } + + it('should make a topologically sorted array', () => { + const out = topoSort(fixture1, (node, inheritance) => inheritance[node]) + // This basically checks all ordering that matters + expect(out.indexOf('layerA')).to.be.below(out.indexOf('layer1A')) + expect(out.indexOf('layer1A')).to.be.below(out.indexOf('layer2A')) + expect(out.indexOf('layerB')).to.be.below(out.indexOf('layer1B')) + expect(out.indexOf('layer1B')).to.be.below(out.indexOf('layer2B')) + expect(out.indexOf('layer2A')).to.be.below(out.indexOf('layer3AB')) + expect(out.indexOf('layer2B')).to.be.below(out.indexOf('layer3AB')) + }) + + it('order in object shouldn\'t matter', () => { + const out = topoSort(fixture2, (node, inheritance) => inheritance[node]) + // This basically checks all ordering that matters + expect(out.indexOf('layerA')).to.be.below(out.indexOf('layer1A')) + expect(out.indexOf('layer1A')).to.be.below(out.indexOf('layer2A')) + expect(out.indexOf('layerB')).to.be.below(out.indexOf('layer1B')) + expect(out.indexOf('layer1B')).to.be.below(out.indexOf('layer2B')) + expect(out.indexOf('layer2A')).to.be.below(out.indexOf('layer3AB')) + expect(out.indexOf('layer2B')).to.be.below(out.indexOf('layer3AB')) + }) + it('ignores cyclic dependencies', () => { + const out = topoSort({ a: 'b', b: 'a', c: 'a' }, (node, inheritance) => inheritance[node]) + expect(out.indexOf('a')).to.be.below(out.indexOf('c')) + }) +}) -- cgit v1.2.3-70-g09d2 From 21d9c87b344598c457ae01b872b85c033a5e043f Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Sun, 12 Jan 2020 23:05:32 +0200 Subject: fix tests --- .../services/style_setter/style_setter.spec.js | 79 ---------------------- .../specs/services/theme_data/theme_data.spec.js | 79 ++++++++++++++++++++++ 2 files changed, 79 insertions(+), 79 deletions(-) delete mode 100644 test/unit/specs/services/style_setter/style_setter.spec.js create mode 100644 test/unit/specs/services/theme_data/theme_data.spec.js (limited to 'test') diff --git a/test/unit/specs/services/style_setter/style_setter.spec.js b/test/unit/specs/services/style_setter/style_setter.spec.js deleted file mode 100644 index 7f789124..00000000 --- a/test/unit/specs/services/style_setter/style_setter.spec.js +++ /dev/null @@ -1,79 +0,0 @@ -import { getLayersArray, topoSort } from 'src/services/style_setter/style_setter' - -describe('getLayersArray', () => { - const fixture = { - layer1: null, - layer2: 'layer1', - layer3a: 'layer2', - layer3b: 'layer2' - } - - it('should expand layers properly (3b)', () => { - const out = getLayersArray('layer3b', fixture) - expect(out).to.eql(['layer1', 'layer2', 'layer3b']) - }) - - it('should expand layers properly (3a)', () => { - const out = getLayersArray('layer3a', fixture) - expect(out).to.eql(['layer1', 'layer2', 'layer3a']) - }) - - it('should expand layers properly (2)', () => { - const out = getLayersArray('layer2', fixture) - expect(out).to.eql(['layer1', 'layer2']) - }) - - it('should expand layers properly (1)', () => { - const out = getLayersArray('layer1', fixture) - expect(out).to.eql(['layer1']) - }) -}) - -describe('topoSort', () => { - const fixture1 = { - layerA: [], - layer1A: ['layerA'], - layer2A: ['layer1A'], - layerB: [], - layer1B: ['layerB'], - layer2B: ['layer1B'], - layer3AB: ['layer2B', 'layer2A'] - } - - // Same thing but messed up order - const fixture2 = { - layer1A: ['layerA'], - layer1B: ['layerB'], - layer2A: ['layer1A'], - layerB: [], - layer3AB: ['layer2B', 'layer2A'], - layer2B: ['layer1B'], - layerA: [] - } - - it('should make a topologically sorted array', () => { - const out = topoSort(fixture1, (node, inheritance) => inheritance[node]) - // This basically checks all ordering that matters - expect(out.indexOf('layerA')).to.be.below(out.indexOf('layer1A')) - expect(out.indexOf('layer1A')).to.be.below(out.indexOf('layer2A')) - expect(out.indexOf('layerB')).to.be.below(out.indexOf('layer1B')) - expect(out.indexOf('layer1B')).to.be.below(out.indexOf('layer2B')) - expect(out.indexOf('layer2A')).to.be.below(out.indexOf('layer3AB')) - expect(out.indexOf('layer2B')).to.be.below(out.indexOf('layer3AB')) - }) - - it('order in object shouldn\'t matter', () => { - const out = topoSort(fixture2, (node, inheritance) => inheritance[node]) - // This basically checks all ordering that matters - expect(out.indexOf('layerA')).to.be.below(out.indexOf('layer1A')) - expect(out.indexOf('layer1A')).to.be.below(out.indexOf('layer2A')) - expect(out.indexOf('layerB')).to.be.below(out.indexOf('layer1B')) - expect(out.indexOf('layer1B')).to.be.below(out.indexOf('layer2B')) - expect(out.indexOf('layer2A')).to.be.below(out.indexOf('layer3AB')) - expect(out.indexOf('layer2B')).to.be.below(out.indexOf('layer3AB')) - }) - it('ignores cyclic dependencies', () => { - const out = topoSort({ a: 'b', b: 'a', c: 'a' }, (node, inheritance) => inheritance[node]) - expect(out.indexOf('a')).to.be.below(out.indexOf('c')) - }) -}) diff --git a/test/unit/specs/services/theme_data/theme_data.spec.js b/test/unit/specs/services/theme_data/theme_data.spec.js new file mode 100644 index 00000000..507905eb --- /dev/null +++ b/test/unit/specs/services/theme_data/theme_data.spec.js @@ -0,0 +1,79 @@ +import { getLayersArray, topoSort } from 'src/services/theme_data/theme_data.service.js' + +describe('getLayersArray', () => { + const fixture = { + layer1: null, + layer2: 'layer1', + layer3a: 'layer2', + layer3b: 'layer2' + } + + it('should expand layers properly (3b)', () => { + const out = getLayersArray('layer3b', fixture) + expect(out).to.eql(['layer1', 'layer2', 'layer3b']) + }) + + it('should expand layers properly (3a)', () => { + const out = getLayersArray('layer3a', fixture) + expect(out).to.eql(['layer1', 'layer2', 'layer3a']) + }) + + it('should expand layers properly (2)', () => { + const out = getLayersArray('layer2', fixture) + expect(out).to.eql(['layer1', 'layer2']) + }) + + it('should expand layers properly (1)', () => { + const out = getLayersArray('layer1', fixture) + expect(out).to.eql(['layer1']) + }) +}) + +describe('topoSort', () => { + const fixture1 = { + layerA: [], + layer1A: ['layerA'], + layer2A: ['layer1A'], + layerB: [], + layer1B: ['layerB'], + layer2B: ['layer1B'], + layer3AB: ['layer2B', 'layer2A'] + } + + // Same thing but messed up order + const fixture2 = { + layer1A: ['layerA'], + layer1B: ['layerB'], + layer2A: ['layer1A'], + layerB: [], + layer3AB: ['layer2B', 'layer2A'], + layer2B: ['layer1B'], + layerA: [] + } + + it('should make a topologically sorted array', () => { + const out = topoSort(fixture1, (node, inheritance) => inheritance[node]) + // This basically checks all ordering that matters + expect(out.indexOf('layerA')).to.be.below(out.indexOf('layer1A')) + expect(out.indexOf('layer1A')).to.be.below(out.indexOf('layer2A')) + expect(out.indexOf('layerB')).to.be.below(out.indexOf('layer1B')) + expect(out.indexOf('layer1B')).to.be.below(out.indexOf('layer2B')) + expect(out.indexOf('layer2A')).to.be.below(out.indexOf('layer3AB')) + expect(out.indexOf('layer2B')).to.be.below(out.indexOf('layer3AB')) + }) + + it('order in object shouldn\'t matter', () => { + const out = topoSort(fixture2, (node, inheritance) => inheritance[node]) + // This basically checks all ordering that matters + expect(out.indexOf('layerA')).to.be.below(out.indexOf('layer1A')) + expect(out.indexOf('layer1A')).to.be.below(out.indexOf('layer2A')) + expect(out.indexOf('layerB')).to.be.below(out.indexOf('layer1B')) + expect(out.indexOf('layer1B')).to.be.below(out.indexOf('layer2B')) + expect(out.indexOf('layer2A')).to.be.below(out.indexOf('layer3AB')) + expect(out.indexOf('layer2B')).to.be.below(out.indexOf('layer3AB')) + }) + it('ignores cyclic dependencies', () => { + const out = topoSort({ a: 'b', b: 'a', c: 'a' }, (node, inheritance) => inheritance[node]) + expect(out.indexOf('a')).to.be.below(out.indexOf('c')) + }) +}) -- cgit v1.2.3-70-g09d2 From 86380f042976557d5260a3f5c2de0a9b0bcdbac6 Mon Sep 17 00:00:00 2001 From: Shpuld Shpludson Date: Tue, 14 Jan 2020 13:28:57 +0000 Subject: Optimize Notifications Rendering --- CHANGELOG.md | 2 ++ src/components/notifications/notifications.js | 27 ++++++++++++++++++---- src/components/notifications/notifications.vue | 2 +- .../notification_utils/notification_utils.js | 4 ++-- .../notification_utils/notification_utils.spec.js | 4 ++-- 5 files changed, 30 insertions(+), 9 deletions(-) (limited to 'test') diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e17dc64..9f5a9305 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] +### Changed +- Notifications column now cleans itself up to optimize performance when tab is left open for a long time ### Fixed - Single notifications left unread when hitting read on another device/tab diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js index a89c0cdc..26ffbab6 100644 --- a/src/components/notifications/notifications.js +++ b/src/components/notifications/notifications.js @@ -2,10 +2,12 @@ import Notification from '../notification/notification.vue' import notificationsFetcher from '../../services/notifications_fetcher/notifications_fetcher.service.js' import { notificationsFromStore, - visibleNotificationsFromStore, + filteredNotificationsFromStore, unseenNotificationsFromStore } from '../../services/notification_utils/notification_utils.js' +const DEFAULT_SEEN_TO_DISPLAY_COUNT = 30 + const Notifications = { props: { // Disables display of panel header @@ -18,7 +20,11 @@ const Notifications = { }, data () { return { - bottomedOut: false + bottomedOut: false, + // How many seen notifications to display in the list. The more there are, + // the heavier the page becomes. This count is increased when loading + // older notifications, and cut back to default whenever hitting "Read!". + seenToDisplayCount: DEFAULT_SEEN_TO_DISPLAY_COUNT } }, computed: { @@ -34,14 +40,17 @@ const Notifications = { unseenNotifications () { return unseenNotificationsFromStore(this.$store) }, - visibleNotifications () { - return visibleNotificationsFromStore(this.$store, this.filterMode) + filteredNotifications () { + return filteredNotificationsFromStore(this.$store, this.filterMode) }, unseenCount () { return this.unseenNotifications.length }, loading () { return this.$store.state.statuses.notifications.loading + }, + notificationsToDisplay () { + return this.filteredNotifications.slice(0, this.unseenCount + this.seenToDisplayCount) } }, components: { @@ -64,12 +73,21 @@ const Notifications = { methods: { markAsSeen () { this.$store.dispatch('markNotificationsAsSeen') + this.seenToDisplayCount = DEFAULT_SEEN_TO_DISPLAY_COUNT }, fetchOlderNotifications () { if (this.loading) { return } + const seenCount = this.filteredNotifications.length - this.unseenCount + if (this.seenToDisplayCount < seenCount) { + this.seenToDisplayCount = Math.min(this.seenToDisplayCount + 20, seenCount) + return + } else if (this.seenToDisplayCount > seenCount) { + this.seenToDisplayCount = seenCount + } + const store = this.$store const credentials = store.state.users.currentUser.credentials store.commit('setNotificationsLoading', { value: true }) @@ -82,6 +100,7 @@ const Notifications = { if (notifs.length === 0) { this.bottomedOut = true } + this.seenToDisplayCount += notifs.length }) } } diff --git a/src/components/notifications/notifications.vue b/src/components/notifications/notifications.vue index c42c35e6..d477a41b 100644 --- a/src/components/notifications/notifications.vue +++ b/src/components/notifications/notifications.vue @@ -32,7 +32,7 @@
{ } } -export const visibleNotificationsFromStore = (store, types) => { +export const filteredNotificationsFromStore = (store, types) => { // map is just to clone the array since sort mutates it and it causes some issues let sortedNotifications = notificationsFromStore(store).map(_ => _).sort(sortById) sortedNotifications = sortBy(sortedNotifications, 'seen') @@ -36,4 +36,4 @@ export const visibleNotificationsFromStore = (store, types) => { } export const unseenNotificationsFromStore = store => - filter(visibleNotificationsFromStore(store), ({ seen }) => !seen) + filter(filteredNotificationsFromStore(store), ({ seen }) => !seen) diff --git a/test/unit/specs/services/notification_utils/notification_utils.spec.js b/test/unit/specs/services/notification_utils/notification_utils.spec.js index 1baa5fc9..00628d83 100644 --- a/test/unit/specs/services/notification_utils/notification_utils.spec.js +++ b/test/unit/specs/services/notification_utils/notification_utils.spec.js @@ -1,7 +1,7 @@ import * as NotificationUtils from 'src/services/notification_utils/notification_utils.js' describe('NotificationUtils', () => { - describe('visibleNotificationsFromStore', () => { + describe('filteredNotificationsFromStore', () => { it('should return sorted notifications with configured types', () => { const store = { state: { @@ -47,7 +47,7 @@ describe('NotificationUtils', () => { type: 'like' } ] - expect(NotificationUtils.visibleNotificationsFromStore(store)).to.eql(expected) + expect(NotificationUtils.filteredNotificationsFromStore(store)).to.eql(expected) }) }) -- cgit v1.2.3-70-g09d2 From 2b36a62c5600738737e0fda4e0fc92e6e1f59c33 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Mon, 20 Jan 2020 01:44:11 +0200 Subject: fix tests, integrate depenentless sorting into toposort for easier testing and better guarantees --- src/services/theme_data/theme_data.service.js | 22 +++++++++++----------- .../specs/services/theme_data/theme_data.spec.js | 10 +++++++++- 2 files changed, 20 insertions(+), 12 deletions(-) (limited to 'test') diff --git a/src/services/theme_data/theme_data.service.js b/src/services/theme_data/theme_data.service.js index 36837b44..63bfb5af 100644 --- a/src/services/theme_data/theme_data.service.js +++ b/src/services/theme_data/theme_data.service.js @@ -540,7 +540,7 @@ const getDependencies = (key, inheritance) => { * @property {Function} getDeps - function that returns dependencies for * given value and inheritance object. * @returns {String[]} keys of inheritance object, sorted in topological - * order + * order. Additionally, dependency-less nodes will always be first in line */ export const topoSort = ( inheritance = SLOT_INHERITANCE, @@ -579,7 +579,14 @@ export const topoSort = ( while (unprocessed.length > 0) { step(unprocessed.pop()) } - return output + return output.sort((a, b) => { + const depsA = getDeps(a, inheritance).length + const depsB = getDeps(b, inheritance).length + + if (depsA === depsB || (depsB !== 0 && depsA !== 0)) return 0 + if (depsA === 0 && depsB !== 0) return -1 + if (depsB === 0 && depsA !== 0) return 1 + }) } /** @@ -657,20 +664,13 @@ export const getLayerSlot = ( } /** - * topologically sorted SLOT_INHERITANCE + additional priority sort + * topologically sorted SLOT_INHERITANCE */ export const SLOT_ORDERED = topoSort( Object.entries(SLOT_INHERITANCE) .sort(([aK, aV], [bK, bV]) => ((aV && aV.priority) || 0) - ((bV && bV.priority) || 0)) .reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {}) -).sort((a, b) => { - const depsA = getDependencies(a, SLOT_INHERITANCE).length - const depsB = getDependencies(b, SLOT_INHERITANCE).length - - if (depsA === depsB || (depsB !== 0 && depsA !== 0)) return 0 - if (depsA === 0 && depsB !== 0) return -1 - if (depsB === 0 && depsA !== 0) return 1 -}) +) /** * Dictionary where keys are color slots and values are opacity associated diff --git a/test/unit/specs/services/theme_data/theme_data.spec.js b/test/unit/specs/services/theme_data/theme_data.spec.js index 507905eb..d8a04ba7 100644 --- a/test/unit/specs/services/theme_data/theme_data.spec.js +++ b/test/unit/specs/services/theme_data/theme_data.spec.js @@ -72,8 +72,16 @@ describe('topoSort', () => { expect(out.indexOf('layer2A')).to.be.below(out.indexOf('layer3AB')) expect(out.indexOf('layer2B')).to.be.below(out.indexOf('layer3AB')) }) + + it('dependentless nodes should be first', () => { + const out = topoSort(fixture2, (node, inheritance) => inheritance[node]) + // This basically checks all ordering that matters + expect(out.indexOf('layerA')).to.eql(0) + expect(out.indexOf('layerB')).to.eql(1) + }) + it('ignores cyclic dependencies', () => { - const out = topoSort({ a: 'b', b: 'a', c: 'a' }, (node, inheritance) => inheritance[node]) + const out = topoSort({ a: 'b', b: 'a', c: 'a' }, (node, inheritance) => [inheritance[node]]) expect(out.indexOf('a')).to.be.below(out.indexOf('c')) }) }) -- cgit v1.2.3-70-g09d2 From a018ea622c4ae34fd204e840b20aba53f84cd051 Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Sun, 26 Jan 2020 15:45:12 +0200 Subject: change emoji reactions to use new format --- src/components/conversation/conversation.js | 2 +- src/components/emoji_reactions/emoji_reactions.js | 9 ++- src/components/emoji_reactions/emoji_reactions.vue | 12 ++-- src/components/status/status.vue | 1 - src/modules/statuses.js | 66 +++++++++++++--------- .../entity_normalizer/entity_normalizer.service.js | 1 + test/unit/specs/modules/statuses.spec.js | 45 +++++++++++++++ 7 files changed, 99 insertions(+), 37 deletions(-) (limited to 'test') diff --git a/src/components/conversation/conversation.js b/src/components/conversation/conversation.js index 7ff0ac08..45fb2bf6 100644 --- a/src/components/conversation/conversation.js +++ b/src/components/conversation/conversation.js @@ -150,7 +150,7 @@ const conversation = { if (!id) return this.highlight = id this.$store.dispatch('fetchFavsAndRepeats', id) - this.$store.dispatch('fetchEmojiReactions', id) + this.$store.dispatch('fetchEmojiReactionsBy', id) }, getHighlight () { return this.isExpanded ? this.highlight : null diff --git a/src/components/emoji_reactions/emoji_reactions.js b/src/components/emoji_reactions/emoji_reactions.js index e81e6e25..b37cce3d 100644 --- a/src/components/emoji_reactions/emoji_reactions.js +++ b/src/components/emoji_reactions/emoji_reactions.js @@ -4,12 +4,17 @@ const EmojiReactions = { props: ['status'], computed: { emojiReactions () { - return this.status.emojiReactions + console.log(this.status.emoji_reactions) + return this.status.emoji_reactions } }, methods: { reactedWith (emoji) { - return this.status.reactedWithEmoji.includes(emoji) + // return [] + const user = this.$store.state.users.currentUser + const reaction = this.status.emoji_reactions.find(r => r.emoji === emoji) + console.log(reaction) + return reaction.accounts && reaction.accounts.find(u => u.id === user.id) }, reactWith (emoji) { this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji }) diff --git a/src/components/emoji_reactions/emoji_reactions.vue b/src/components/emoji_reactions/emoji_reactions.vue index d83f60b6..8a229240 100644 --- a/src/components/emoji_reactions/emoji_reactions.vue +++ b/src/components/emoji_reactions/emoji_reactions.vue @@ -1,14 +1,14 @@ diff --git a/src/components/status/status.vue b/src/components/status/status.vue index 87e8b5da..d5739304 100644 --- a/src/components/status/status.vue +++ b/src/components/status/status.vue @@ -355,7 +355,6 @@ diff --git a/src/modules/statuses.js b/src/modules/statuses.js index dbae9d38..ea0c1749 100644 --- a/src/modules/statuses.js +++ b/src/modules/statuses.js @@ -10,10 +10,7 @@ import { first, last, isArray, - omitBy, - flow, - filter, - keys + omitBy } from 'lodash' import { set } from 'vue' import apiService from '../services/api/api.service.js' @@ -534,33 +531,48 @@ export const mutations = { newStatus.fave_num = newStatus.favoritedBy.length newStatus.favorited = !!newStatus.favoritedBy.find(({ id }) => currentUser.id === id) }, - addEmojiReactions (state, { id, emojiReactions, currentUser }) { + addEmojiReactionsBy (state, { id, emojiReactions, currentUser }) { const status = state.allStatusesObject[id] - set(status, 'emojiReactions', emojiReactions) - const reactedWithEmoji = flow( - keys, - filter(reaction => find(reaction, { id: currentUser.id })) - )(emojiReactions) - set(status, 'reactedWithEmoji', reactedWithEmoji) + set(status, 'emoji_reactions', emojiReactions) }, addOwnReaction (state, { id, emoji, currentUser }) { const status = state.allStatusesObject[id] - status.emojiReactions = status.emojiReactions || {} - const listOfUsers = (status.emojiReactions && status.emojiReactions[emoji]) || [] - const hasSelfAlready = !!find(listOfUsers, { id: currentUser.id }) - if (!hasSelfAlready) { - set(status.emojiReactions, emoji, listOfUsers.concat([{ id: currentUser.id }])) - set(status, 'reactedWithEmoji', [...status.reactedWithEmoji, emoji]) + const reactionIndex = findIndex(status.emoji_reactions, { emoji }) + const reaction = status.emoji_reactions[reactionIndex] || { emoji, count: 0, accounts: [] } + + const newReaction = { + ...reaction, + count: reaction.count + 1, + accounts: [ + ...reaction.accounts, + currentUser + ] + } + + // Update count of existing reaction if it exists, otherwise append at the end + if (reactionIndex >= 0) { + set(status.emoji_reactions, reactionIndex, newReaction) + } else { + set(status, 'emoji_reactions', [...status.emoji_reactions, newReaction]) } }, removeOwnReaction (state, { id, emoji, currentUser }) { const status = state.allStatusesObject[id] - const listOfUsers = status.emojiReactions[emoji] || [] - const hasSelfAlready = !!find(listOfUsers, { id: currentUser.id }) - if (hasSelfAlready) { - const newUsers = filter(listOfUsers, user => user.id !== currentUser.id) - set(status.emojiReactions, emoji, newUsers) - set(status, 'reactedWithEmoji', status.reactedWithEmoji.filter(e => e !== emoji)) + const reactionIndex = findIndex(status.emoji_reactions, { emoji }) + if (reactionIndex < 0) return + + const reaction = status.emoji_reactions[reactionIndex] + + const newReaction = { + ...reaction, + count: reaction.count - 1, + accounts: reaction.accounts.filter(acc => acc.id === currentUser.id) + } + + if (newReaction.count > 0) { + set(status.emoji_reactions, reactionIndex, newReaction) + } else { + set(status, 'emoji_reactions', status.emoji_reactions.filter(r => r.emoji !== emoji)) } }, updateStatusWithPoll (state, { id, poll }) { @@ -672,7 +684,7 @@ const statuses = { commit('addOwnReaction', { id, emoji, currentUser }) rootState.api.backendInteractor.reactWithEmoji({ id, emoji }).then( status => { - dispatch('fetchEmojiReactions', id) + dispatch('fetchEmojiReactionsBy', id) } ) }, @@ -681,14 +693,14 @@ const statuses = { commit('removeOwnReaction', { id, emoji, currentUser }) rootState.api.backendInteractor.unreactWithEmoji({ id, emoji }).then( status => { - dispatch('fetchEmojiReactions', id) + dispatch('fetchEmojiReactionsBy', id) } ) }, - fetchEmojiReactions ({ rootState, commit }, id) { + fetchEmojiReactionsBy ({ rootState, commit }, id) { rootState.api.backendInteractor.fetchEmojiReactions({ id }).then( emojiReactions => { - commit('addEmojiReactions', { id, emojiReactions, currentUser: rootState.users.currentUser }) + commit('addEmojiReactionsBy', { id, emojiReactions, currentUser: rootState.users.currentUser }) } ) }, diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js index ee007bee..03eaa5d7 100644 --- a/src/services/entity_normalizer/entity_normalizer.service.js +++ b/src/services/entity_normalizer/entity_normalizer.service.js @@ -233,6 +233,7 @@ export const parseStatus = (data) => { output.statusnet_html = addEmojis(data.content, data.emojis) output.tags = data.tags + output.emoji_reactions = [{ emoji: 'A', count: 5 }] // data.pleroma.emoji_reactions if (data.pleroma) { const { pleroma } = data diff --git a/test/unit/specs/modules/statuses.spec.js b/test/unit/specs/modules/statuses.spec.js index f794997b..e53aa388 100644 --- a/test/unit/specs/modules/statuses.spec.js +++ b/test/unit/specs/modules/statuses.spec.js @@ -241,6 +241,51 @@ describe('Statuses module', () => { }) }) + describe('emojiReactions', () => { + it('increments count in existing reaction', () => { + const state = defaultState() + const status = makeMockStatus({ id: '1' }) + status.emoji_reactions = [ { emoji: '😂', count: 1, accounts: [] } ] + + mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) + mutations.addOwnReaction(state, { id: '1', emoji: '😂', currentUser: { id: 'me' } }) + expect(state.allStatusesObject['1'].emoji_reactions[0].count).to.eql(2) + expect(state.allStatusesObject['1'].emoji_reactions[0].accounts[0].id).to.eql('me') + }) + + it('adds a new reaction', () => { + const state = defaultState() + const status = makeMockStatus({ id: '1' }) + status.emoji_reactions = [] + + mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) + mutations.addOwnReaction(state, { id: '1', emoji: '😂', currentUser: { id: 'me' } }) + expect(state.allStatusesObject['1'].emoji_reactions[0].count).to.eql(1) + expect(state.allStatusesObject['1'].emoji_reactions[0].accounts[0].id).to.eql('me') + }) + + it('decreases count in existing reaction', () => { + const state = defaultState() + const status = makeMockStatus({ id: '1' }) + status.emoji_reactions = [ { emoji: '😂', count: 2, accounts: [{ id: 'me' }] } ] + + mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) + mutations.removeOwnReaction(state, { id: '1', emoji: '😂', currentUser: {} }) + expect(state.allStatusesObject['1'].emoji_reactions[0].count).to.eql(1) + expect(state.allStatusesObject['1'].emoji_reactions[0].accounts).to.eql([]) + }) + + it('removes a reaction', () => { + const state = defaultState() + const status = makeMockStatus({ id: '1' }) + status.emoji_reactions = [{ emoji: '😂', count: 1, accounts: [{ id: 'me' }] }] + + mutations.addNewStatuses(state, { statuses: [status], showImmediately: true, timeline: 'public' }) + mutations.removeOwnReaction(state, { id: '1', emoji: '😂', currentUser: {} }) + expect(state.allStatusesObject['1'].emoji_reactions.length).to.eql(0) + }) + }) + describe('showNewStatuses', () => { it('resets the minId to the min of the visible statuses when adding new to visible statuses', () => { const state = defaultState() -- cgit v1.2.3-70-g09d2 From 7c074b87418602effac03c4bae34afffcfca283f Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Mon, 27 Jan 2020 04:20:13 +0200 Subject: fix rgba css generation, add some tests to automatically verify that themes are generated properly --- build/webpack.base.conf.js | 1 + src/services/color_convert/color_convert.js | 2 +- src/services/theme_data/theme_data.service.js | 2 +- static/themes/redmond-xx-se.json | 2 +- static/themes/redmond-xx.json | 2 +- static/themes/redmond-xxi.json | 2 +- .../services/theme_data/sanity_checks.spec.js | 28 ++++ .../specs/services/theme_data/theme_data.spec.js | 147 +++++++++++---------- 8 files changed, 109 insertions(+), 77 deletions(-) create mode 100644 test/unit/specs/services/theme_data/sanity_checks.spec.js (limited to 'test') diff --git a/build/webpack.base.conf.js b/build/webpack.base.conf.js index 5cc0135e..dfef37a6 100644 --- a/build/webpack.base.conf.js +++ b/build/webpack.base.conf.js @@ -35,6 +35,7 @@ module.exports = { ], alias: { 'vue$': 'vue/dist/vue.runtime.common', + 'static': path.resolve(__dirname, '../static'), 'src': path.resolve(__dirname, '../src'), 'assets': path.resolve(__dirname, '../src/assets'), 'components': path.resolve(__dirname, '../src/components') diff --git a/src/services/color_convert/color_convert.js b/src/services/color_convert/color_convert.js index 0bf8f646..b5461038 100644 --- a/src/services/color_convert/color_convert.js +++ b/src/services/color_convert/color_convert.js @@ -171,7 +171,7 @@ export const mixrgb = (a, b) => { * @returns {String} CSS rgba() color */ export const rgba2css = function (rgba) { - return `rgba(${rgba.r}, ${rgba.g}, ${rgba.b}, ${rgba.a})` + return `rgba(${Math.floor(rgba.r)}, ${Math.floor(rgba.g)}, ${Math.floor(rgba.b)}, .5)` } /** diff --git a/src/services/theme_data/theme_data.service.js b/src/services/theme_data/theme_data.service.js index 0a6733a9..49b99148 100644 --- a/src/services/theme_data/theme_data.service.js +++ b/src/services/theme_data/theme_data.service.js @@ -353,7 +353,7 @@ export const getColors = (sourceColors, sourceOpacity, mod) => SLOT_ORDERED.redu if (dependencySlot && sourceColors[dependencySlot] === 'transparent') { outputColor.a = 0 } else { - outputColor.a = sourceOpacity[opacitySlot] || OPACITIES[opacitySlot].defaultValue || 1 + outputColor.a = Number(sourceOpacity[opacitySlot]) || OPACITIES[opacitySlot].defaultValue || 1 } } if (opacitySlot) { diff --git a/static/themes/redmond-xx-se.json b/static/themes/redmond-xx-se.json index 1a867fcc..8deab7b7 100644 --- a/static/themes/redmond-xx-se.json +++ b/static/themes/redmond-xx-se.json @@ -1,7 +1,7 @@ { "_pleroma_theme_version": 2, "name": "Redmond XX SE", - "theme": { + "source": { "shadows": { "panel": [ { diff --git a/static/themes/redmond-xx.json b/static/themes/redmond-xx.json index 78c92f10..cdb89fe3 100644 --- a/static/themes/redmond-xx.json +++ b/static/themes/redmond-xx.json @@ -1,7 +1,7 @@ { "_pleroma_theme_version": 2, "name": "Redmond XX", - "theme": { + "source": { "shadows": { "panel": [ { diff --git a/static/themes/redmond-xxi.json b/static/themes/redmond-xxi.json index 28f68351..79a2cb26 100644 --- a/static/themes/redmond-xxi.json +++ b/static/themes/redmond-xxi.json @@ -1,7 +1,7 @@ { "_pleroma_theme_version": 2, "name": "Redmond XXI", - "theme": { + "source": { "shadows": { "panel": [ { diff --git a/test/unit/specs/services/theme_data/sanity_checks.spec.js b/test/unit/specs/services/theme_data/sanity_checks.spec.js new file mode 100644 index 00000000..f0072e7d --- /dev/null +++ b/test/unit/specs/services/theme_data/sanity_checks.spec.js @@ -0,0 +1,28 @@ +import { getColors } from 'src/services/theme_data/theme_data.service.js' + +const checkColors = (output) => { + expect(output).to.have.property('colors') + Object.entries(output.colors).forEach(([key, v]) => { + expect(v, key).to.be.an('object') + expect(v, key).to.include.all.keys('r', 'g', 'b') + 'rgba'.split('').forEach(k => { + if ((k === 'a' && v.hasOwnProperty('a')) || k !== 'a') { + expect(v[k], key + '.' + k).to.be.a('number') + expect(v[k], key + '.' + k).to.be.least(0) + expect(v[k], key + '.' + k).to.be.most(k === 'a' ? 1 : 255) + } + }) + }) +} + +describe('Theme Data utility functions', () => { + const context = require.context('static/themes/', false, /\.json$/) + context.keys().forEach((key) => { + it(`Should render all colors for ${key} properly`, () => { + const { theme, source } = context(key) + const data = source || theme + const colors = getColors(data.colors, data.opacity, 1) + checkColors(colors) + }) + }) +}) diff --git a/test/unit/specs/services/theme_data/theme_data.spec.js b/test/unit/specs/services/theme_data/theme_data.spec.js index d8a04ba7..67f4fd5a 100644 --- a/test/unit/specs/services/theme_data/theme_data.spec.js +++ b/test/unit/specs/services/theme_data/theme_data.spec.js @@ -1,87 +1,90 @@ import { getLayersArray, topoSort } from 'src/services/theme_data/theme_data.service.js' -describe('getLayersArray', () => { - const fixture = { - layer1: null, - layer2: 'layer1', - layer3a: 'layer2', - layer3b: 'layer2' - } +describe('Theme Data utility functions', () => { + describe('getLayersArray', () => { + const fixture = { + layer1: null, + layer2: 'layer1', + layer3a: 'layer2', + layer3b: 'layer2' + } - it('should expand layers properly (3b)', () => { - const out = getLayersArray('layer3b', fixture) - expect(out).to.eql(['layer1', 'layer2', 'layer3b']) - }) + it('should expand layers properly (3b)', () => { + const out = getLayersArray('layer3b', fixture) + expect(out).to.eql(['layer1', 'layer2', 'layer3b']) + }) - it('should expand layers properly (3a)', () => { - const out = getLayersArray('layer3a', fixture) - expect(out).to.eql(['layer1', 'layer2', 'layer3a']) - }) + it('should expand layers properly (3a)', () => { + const out = getLayersArray('layer3a', fixture) + expect(out).to.eql(['layer1', 'layer2', 'layer3a']) + }) - it('should expand layers properly (2)', () => { - const out = getLayersArray('layer2', fixture) - expect(out).to.eql(['layer1', 'layer2']) - }) + it('should expand layers properly (2)', () => { + const out = getLayersArray('layer2', fixture) + expect(out).to.eql(['layer1', 'layer2']) + }) - it('should expand layers properly (1)', () => { - const out = getLayersArray('layer1', fixture) - expect(out).to.eql(['layer1']) + it('should expand layers properly (1)', () => { + const out = getLayersArray('layer1', fixture) + expect(out).to.eql(['layer1']) + }) }) -}) -describe('topoSort', () => { - const fixture1 = { - layerA: [], - layer1A: ['layerA'], - layer2A: ['layer1A'], - layerB: [], - layer1B: ['layerB'], - layer2B: ['layer1B'], - layer3AB: ['layer2B', 'layer2A'] - } + describe('topoSort', () => { + const fixture1 = { + layerA: [], + layer1A: ['layerA'], + layer2A: ['layer1A'], + layerB: [], + layer1B: ['layerB'], + layer2B: ['layer1B'], + layer3AB: ['layer2B', 'layer2A'] + } - // Same thing but messed up order - const fixture2 = { - layer1A: ['layerA'], - layer1B: ['layerB'], - layer2A: ['layer1A'], - layerB: [], - layer3AB: ['layer2B', 'layer2A'], - layer2B: ['layer1B'], - layerA: [] - } + // Same thing but messed up order + const fixture2 = { + layer1A: ['layerA'], + layer1B: ['layerB'], + layer2A: ['layer1A'], + layerB: [], + layer3AB: ['layer2B', 'layer2A'], + layer2B: ['layer1B'], + layerA: [] + } - it('should make a topologically sorted array', () => { - const out = topoSort(fixture1, (node, inheritance) => inheritance[node]) - // This basically checks all ordering that matters - expect(out.indexOf('layerA')).to.be.below(out.indexOf('layer1A')) - expect(out.indexOf('layer1A')).to.be.below(out.indexOf('layer2A')) - expect(out.indexOf('layerB')).to.be.below(out.indexOf('layer1B')) - expect(out.indexOf('layer1B')).to.be.below(out.indexOf('layer2B')) - expect(out.indexOf('layer2A')).to.be.below(out.indexOf('layer3AB')) - expect(out.indexOf('layer2B')).to.be.below(out.indexOf('layer3AB')) - }) + it('should make a topologically sorted array', () => { + const out = topoSort(fixture1, (node, inheritance) => inheritance[node]) + // This basically checks all ordering that matters + expect(out.indexOf('layerA')).to.be.below(out.indexOf('layer1A')) + expect(out.indexOf('layer1A')).to.be.below(out.indexOf('layer2A')) + expect(out.indexOf('layerB')).to.be.below(out.indexOf('layer1B')) + expect(out.indexOf('layer1B')).to.be.below(out.indexOf('layer2B')) + expect(out.indexOf('layer2A')).to.be.below(out.indexOf('layer3AB')) + expect(out.indexOf('layer2B')).to.be.below(out.indexOf('layer3AB')) + }) - it('order in object shouldn\'t matter', () => { - const out = topoSort(fixture2, (node, inheritance) => inheritance[node]) - // This basically checks all ordering that matters - expect(out.indexOf('layerA')).to.be.below(out.indexOf('layer1A')) - expect(out.indexOf('layer1A')).to.be.below(out.indexOf('layer2A')) - expect(out.indexOf('layerB')).to.be.below(out.indexOf('layer1B')) - expect(out.indexOf('layer1B')).to.be.below(out.indexOf('layer2B')) - expect(out.indexOf('layer2A')).to.be.below(out.indexOf('layer3AB')) - expect(out.indexOf('layer2B')).to.be.below(out.indexOf('layer3AB')) - }) + it('order in object shouldn\'t matter', () => { + const out = topoSort(fixture2, (node, inheritance) => inheritance[node]) + // This basically checks all ordering that matters + expect(out.indexOf('layerA')).to.be.below(out.indexOf('layer1A')) + expect(out.indexOf('layer1A')).to.be.below(out.indexOf('layer2A')) + expect(out.indexOf('layerB')).to.be.below(out.indexOf('layer1B')) + expect(out.indexOf('layer1B')).to.be.below(out.indexOf('layer2B')) + expect(out.indexOf('layer2A')).to.be.below(out.indexOf('layer3AB')) + expect(out.indexOf('layer2B')).to.be.below(out.indexOf('layer3AB')) + }) - it('dependentless nodes should be first', () => { - const out = topoSort(fixture2, (node, inheritance) => inheritance[node]) - // This basically checks all ordering that matters - expect(out.indexOf('layerA')).to.eql(0) - expect(out.indexOf('layerB')).to.eql(1) - }) + it('dependentless nodes should be first', () => { + const out = topoSort(fixture2, (node, inheritance) => inheritance[node]) + // This basically checks all ordering that matters + expect(out.indexOf('layerA')).to.eql(0) + expect(out.indexOf('layerB')).to.eql(1) + }) - it('ignores cyclic dependencies', () => { - const out = topoSort({ a: 'b', b: 'a', c: 'a' }, (node, inheritance) => [inheritance[node]]) - expect(out.indexOf('a')).to.be.below(out.indexOf('c')) + it('ignores cyclic dependencies', () => { + const out = topoSort({ a: 'b', b: 'a', c: 'a' }, (node, inheritance) => [inheritance[node]]) + expect(out.indexOf('a')).to.be.below(out.indexOf('c')) + }) }) + }) -- cgit v1.2.3-70-g09d2 From 5313833d80221bbad667aeda39ca9e7321489e30 Mon Sep 17 00:00:00 2001 From: Henry Jameson Date: Mon, 27 Jan 2020 04:24:00 +0200 Subject: lint --- src/components/style_switcher/style_switcher.vue | 137 +++++++++++---------- .../specs/services/theme_data/theme_data.spec.js | 1 - 2 files changed, 70 insertions(+), 68 deletions(-) (limited to 'test') diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue index 1c39a806..6e38bd8e 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/style_switcher/style_switcher.vue @@ -1,74 +1,77 @@