From 7c074b87418602effac03c4bae34afffcfca283f Mon Sep 17 00:00:00 2001
From: Henry Jameson <me@hjkos.com>
Date: Mon, 27 Jan 2020 04:20:13 +0200
Subject: [PATCH] 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 +++
 .../services/theme_data/theme_data.spec.js    | 161 +++++++++---------
 8 files changed, 116 insertions(+), 84 deletions(-)
 create mode 100644 test/unit/specs/services/theme_data/sanity_checks.spec.js

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 (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 (3a)', () => {
-    const out = getLayersArray('layer3a', fixture)
-    expect(out).to.eql(['layer1', 'layer2', 'layer3a'])
+  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('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('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('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'))
-  })
 })