Support for Testem in Ember CLI (#12442)

* DEV: Use custom tags rather than handlebars server side

These will be skipped if they are ever rendered in a document. The
handlebars really messes stuff up.

* DEV: Build our own locale file for testing purposes

We can't practically proxy everything in test mode, but we can
approximate the logic and build our own locale file for testing purposes
that works quite well. This allows us to run tests without a proxy.

* DEV: Support for testem runner for ember cli tests
This commit is contained in:
Robin Ward
2021-03-19 09:32:46 -04:00
committed by GitHub
parent 2d1b087efc
commit 5b02aad9c1
15 changed files with 302 additions and 71 deletions

View File

@@ -1,5 +1,6 @@
<!DOCTYPE html>
{{bootstrap-content-for "html-tag"}}
<bootstrap-content key="html-tag">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
@@ -7,18 +8,18 @@
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
{{bootstrap-content-for "before-script-load"}}
<bootstrap-content key="before-script-load">
{{content-for "before-script-load"}}
<script src="{{rootURL}}assets/vendor.js"></script>
<script src="{{rootURL}}assets/discourse.js"></script>
<script src="{{rootURL}}assets/admin.js"></script>
{{bootstrap-content-for "head"}}
<bootstrap-content key="head">
{{content-for "head"}}
</head>
<body>
{{bootstrap-content-for "body"}}
<bootstrap-content key="body">
{{content-for "body"}}
<section id='main'>
@@ -27,13 +28,13 @@
<div id='offscreen-content'>
</div>
{{bootstrap-content-for "hidden-login-form"}}
{{bootstrap-content-for "preloaded"}}
<bootstrap-content key="hidden-login-form">
<bootstrap-content key="preloaded">
<script src="{{rootURL}}assets/scripts/start-app.js"></script>
<script src="{{rootURL}}assets/scripts/discourse-boot.js"></script>
{{bootstrap-content-for "body-footer"}}
<bootstrap-content key="body-footer">
{{content-for "body-footer"}}
</body>
</html>

View File

@@ -4,26 +4,11 @@ const EmberApp = require("ember-cli/lib/broccoli/ember-app");
const resolve = require("path").resolve;
const mergeTrees = require("broccoli-merge-trees");
const concat = require("broccoli-concat");
const babel = require("broccoli-babel-transpiler");
const path = require("path");
const prettyTextEngine = require("./lib/pretty-text-engine");
const { createI18nTree } = require("./lib/translation-plugin");
const discourseScss = require("./lib/discourse-scss");
const funnel = require("broccoli-funnel");
function prettyTextEngine(vendorJs, engine) {
let engineTree = babel(`../pretty-text/engines/${engine}`, {
plugins: ["@babel/plugin-transform-modules-amd"],
moduleIds: true,
getModuleId(name) {
return `pretty-text/engines/${engine}/${path.basename(name)}`;
},
});
let markdownIt = funnel(vendorJs, { files: ["markdown-it.js"] });
return concat(mergeTrees([engineTree, markdownIt]), {
outputFile: `assets/${engine}.js`,
});
}
module.exports = function (defaults) {
let discourseRoot = resolve("../../../..");
let vendorJs = discourseRoot + "/vendor/assets/javascripts/";
@@ -44,7 +29,14 @@ module.exports = function (defaults) {
app.import(vendorJs + "jquery.autoellipsis-1.0.10.js");
return mergeTrees([
discourseScss(`${discourseRoot}/app/assets/stylesheets`, "testem.scss"),
createI18nTree(discourseRoot, vendorJs),
app.toTree(),
funnel(`${discourseRoot}/public/javascripts`, { destDir: "javascripts" }),
funnel(`${vendorJs}/highlightjs`, {
files: ["highlight-test-bundle.min.js"],
destDir: "assets/highlightjs",
}),
concat(app.options.adminTree, {
outputFile: `assets/admin.js`,
}),

View File

@@ -20,25 +20,6 @@ function htmlTag(buffer, bootstrap) {
buffer.push(`<html lang="${bootstrap.html_lang}"${classList}>`);
}
function bareStylesheets(buffer, bootstrap) {
(bootstrap.stylesheets || []).forEach((s) => {
if (s.theme_id) {
return;
}
let attrs = [];
if (s.media) {
attrs.push(`media="${s.media}"`);
}
if (s.target) {
attrs.push(`data-target="${s.target}"`);
}
let link = `<link rel="stylesheet" type="text/css" href="${
s.href
}" ${attrs.join(" ")}></script>\n`;
buffer.push(link);
});
}
function head(buffer, bootstrap) {
if (bootstrap.csrf_token) {
buffer.push(`<meta name="csrf-param" buffer="authenticity_token">`);
@@ -143,7 +124,6 @@ const BUILDERS = {
preloaded: preloaded,
"body-footer": bodyFooter,
"locale-script": localeScript,
"bare-stylesheets": bareStylesheets,
};
function replaceIn(bootstrap, template, id) {
@@ -151,7 +131,7 @@ function replaceIn(bootstrap, template, id) {
BUILDERS[id](buffer, bootstrap);
let contents = buffer.filter((b) => b && b.length > 0).join("\n");
return template.replace(`{{bootstrap-content-for "${id}"}}`, contents);
return template.replace(`<bootstrap-content key="${id}">`, contents);
}
function applyBootstrap(bootstrap, template) {

View File

@@ -0,0 +1,44 @@
const Plugin = require("broccoli-plugin");
const sass = require("sass");
const fs = require("fs");
const concat = require("broccoli-concat");
let built = false;
class DiscourseScss extends Plugin {
constructor(inputNodes, inputFile, options) {
super(inputNodes, {
...options,
persistentOutput: true,
});
this.inputFile = inputFile;
}
build() {
// We could get fancy eventually and do this based on whether the css changes
// but this is just used for tests right now.
if (built) {
return;
}
let file = this.inputPaths[0] + "/" + this.inputFile;
let result = sass.renderSync({
file,
includePaths: this.inputPaths,
});
fs.writeFileSync(
`${this.outputPath}/` + this.inputFile.replace(".scss", ".css"),
result.css
);
built = true;
}
}
module.exports = function scss(path, file) {
return concat(new DiscourseScss([path], file), {
outputFile: `assets/${file.replace(".scss", ".css")}`,
});
};

View File

@@ -0,0 +1,21 @@
const babel = require("broccoli-babel-transpiler");
const mergeTrees = require("broccoli-merge-trees");
const funnel = require("broccoli-funnel");
const path = require("path");
const concat = require("broccoli-concat");
module.exports = function prettyTextEngine(vendorJs, engine) {
let engineTree = babel(`../pretty-text/engines/${engine}`, {
plugins: ["@babel/plugin-transform-modules-amd"],
moduleIds: true,
getModuleId(name) {
return `pretty-text/engines/${engine}/${path.basename(name)}`;
},
});
let markdownIt = funnel(vendorJs, { files: ["markdown-it.js"] });
return concat(mergeTrees([engineTree, markdownIt]), {
outputFile: `assets/${engine}.js`,
});
};

View File

@@ -0,0 +1,93 @@
const Plugin = require("broccoli-plugin");
const Yaml = require("js-yaml");
const fs = require("fs");
const concat = require("broccoli-concat");
const mergeTrees = require("broccoli-merge-trees");
let built = false;
class TranslationPlugin extends Plugin {
constructor(inputNodes, inputFile, options) {
super(inputNodes, {
...options,
persistentOutput: true,
});
this.inputFile = inputFile;
}
build() {
// We could get fancy eventually and do this based on whether the yaml
// or vendor files change but in practice we should't need exact up to date
// translations in admin.
if (built) {
return;
}
let file = this.inputPaths[0] + "/" + this.inputFile;
let yaml = fs.readFileSync(file, { encoding: "UTF-8" });
let parsed = Yaml.load(yaml);
let extras = {
en: {
admin: parsed.en.admin_js.admin,
},
};
delete parsed.en.admin_js;
delete parsed.en.wizard_js;
let contents = `
I18n.locale = 'en';
I18n.translations = ${JSON.stringify(parsed)};
I18n.extras = ${JSON.stringify(extras)};
I18n._compiledMFs = {};
`;
fs.writeFileSync(
`${this.outputPath}/` + this.inputFile.replace(".yml", ".js"),
contents
);
built = true;
}
}
module.exports = function translatePlugin(...params) {
return new TranslationPlugin(...params);
};
module.exports.createI18nTree = function (discourseRoot, vendorJs) {
let en = new TranslationPlugin(
[discourseRoot + "/config/locales"],
"client.en.yml"
);
return concat(
mergeTrees([
vendorJs,
discourseRoot + "/app/assets/javascripts/locales",
discourseRoot + "/lib/javascripts",
en,
]),
{
inputFiles: [
"i18n.js",
"moment.js",
"moment-timezone-with-data.js",
"messageformat-lookup.js",
"client.en.js",
],
headerFiles: [
"i18n.js",
"moment.js",
"moment-timezone-with-data.js",
"messageformat-lookup.js",
],
footerFiles: ["client.en.js"],
outputFile: `assets/test-i18n.js`,
}
);
};
module.exports.TranslationPlugin = TranslationPlugin;

View File

@@ -41,6 +41,7 @@
"ember-qunit": "^4.6.0",
"ember-source": "~3.15.0",
"html-entities": "^2.1.0",
"js-yaml": "^4.0.0",
"loader.js": "^4.7.0",
"message-bus-client": "^3.3.0",
"mousetrap": "^1.6.5",
@@ -48,6 +49,7 @@
"pretender": "^3.4.3",
"pretty-text": "^1.0.0",
"qunit-dom": "^0.9.2",
"sass": "^1.32.8",
"select-kit": "^1.0.0",
"sinon": "^9.2.0",
"virtual-dom": "^2.1.1"
@@ -62,5 +64,8 @@
"paths": [
"lib/bootstrap-json"
]
},
"dependencies": {
"sass": "^1.32.8"
}
}

View File

@@ -4,17 +4,15 @@ module.exports = {
launch_in_ci: ["Chrome"],
launch_in_dev: ["Chrome"],
browser_args: {
Chrome: {
ci: [
// --no-sandbox is needed when running Chrome inside a container
process.env.CI ? "--no-sandbox" : null,
"--headless",
"--disable-dev-shm-usage",
"--disable-software-rasterizer",
"--mute-audio",
"--remote-debugging-port=0",
"--window-size=1440,900",
].filter(Boolean),
},
Chrome: [
// --no-sandbox is needed when running Chrome inside a container
process.env.CI ? "--no-sandbox" : null,
"--headless",
"--disable-dev-shm-usage",
"--disable-software-rasterizer",
"--mute-audio",
"--remote-debugging-port=4201",
"--window-size=1440,900",
].filter(Boolean),
},
};

View File

@@ -72,12 +72,12 @@ acceptance("flagging", function (needs) {
full_name: null,
});
});
server.put("admin/users/5/silence", () => {
server.put("/admin/users/5/silence", () => {
return helper.response({
silenced: true,
});
});
server.post("post_actions", () => {
server.post("/post_actions", () => {
return helper.response({
response: true,
});

View File

@@ -490,7 +490,7 @@ export function applyDefaultHandlers(pretender) {
});
});
pretender.get("groups", () => {
pretender.get("/groups", () => {
return response(200, fixturesByUrl["/groups.json"]);
});
@@ -498,7 +498,7 @@ export function applyDefaultHandlers(pretender) {
return response(200, fixturesByUrl["/groups.json?username=eviltrout"]);
});
pretender.get("groups/search.json", () => {
pretender.get("/groups/search.json", () => {
return response(200, []);
});

View File

@@ -9,8 +9,12 @@ export default function (helpers) {
if (fixture && fixture.default) {
const obj = fixture.default;
Object.keys(obj).forEach((url) => {
let fixtureUrl = url;
if (fixtureUrl[0] !== "/") {
fixtureUrl = "/" + fixtureUrl;
}
fixturesByUrl[url] = obj[url];
this.get(url, () => response(obj[url]));
this.get(fixtureUrl, () => response(obj[url]));
});
}
}

View File

@@ -14,6 +14,7 @@
<link rel="stylesheet" href="{{rootURL}}assets/vendor.css">
<link rel="stylesheet" href="{{rootURL}}assets/test-support.css">
<link rel="stylesheet" href="{{rootURL}}assets/testem.css">
{{content-for "head-footer"}}
{{content-for "test-head-footer"}}
@@ -27,8 +28,7 @@
}
</style>
{{bootstrap-content-for "locale-script"}}
{{bootstrap-content-for "bare-stylesheets"}}
<script src="{{rootURL}}assets/test-i18n.js"></script>
</head>
<body>
{{content-for "body"}}

View File

@@ -17,7 +17,7 @@ discourseModule("Integration | Component | highlighted-code", function (hooks) {
beforeEach() {
this.session.highlightJsPath =
"assets/highlightjs/highlight-test-bundle.min.js";
"/assets/highlightjs/highlight-test-bundle.min.js";
this.set("code", "def test; end");
},
@@ -34,7 +34,7 @@ discourseModule("Integration | Component | highlighted-code", function (hooks) {
beforeEach() {
this.session.highlightJsPath =
"assets/highlightjs/highlight-test-bundle.min.js";
"/assets/highlightjs/highlight-test-bundle.min.js";
this.set("code", LONG_CODE_BLOCK);
},

View File

@@ -2331,6 +2331,14 @@ anymatch@^2.0.0:
micromatch "^3.1.4"
normalize-path "^2.1.1"
anymatch@~3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142"
integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==
dependencies:
normalize-path "^3.0.0"
picomatch "^2.0.4"
aot-test-generators@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/aot-test-generators/-/aot-test-generators-0.1.0.tgz#43f0f615f97cb298d7919c1b0b4e6b7310b03cd0"
@@ -2358,6 +2366,11 @@ argparse@^1.0.7:
dependencies:
sprintf-js "~1.0.2"
argparse@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
arr-diff@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"
@@ -3248,6 +3261,11 @@ binary-extensions@^1.0.0:
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65"
integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==
binary-extensions@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
"binaryextensions@1 || 2":
version "2.2.0"
resolved "https://registry.yarnpkg.com/binaryextensions/-/binaryextensions-2.2.0.tgz#e7c6ba82d4f5f5758c26078fe8eea28881233311"
@@ -3355,7 +3373,7 @@ braces@^2.3.1, braces@^2.3.2:
split-string "^3.0.2"
to-regex "^3.0.1"
braces@^3.0.1:
braces@^3.0.1, braces@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
@@ -4224,6 +4242,21 @@ charm@^1.0.0:
dependencies:
inherits "^2.0.1"
"chokidar@>=2.0.0 <4.0.0":
version "3.5.1"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a"
integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==
dependencies:
anymatch "~3.1.1"
braces "~3.0.2"
glob-parent "~5.1.0"
is-binary-path "~2.1.0"
is-glob "~4.0.1"
normalize-path "~3.0.0"
readdirp "~3.5.0"
optionalDependencies:
fsevents "~2.3.1"
chokidar@^2.1.8:
version "2.1.8"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917"
@@ -6637,6 +6670,11 @@ fsevents@^1.2.7:
bindings "^1.5.0"
nan "^2.12.1"
fsevents@~2.3.1:
version "2.3.2"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
function-bind@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
@@ -6789,6 +6827,13 @@ glob-parent@^5.1.0:
dependencies:
is-glob "^4.0.1"
glob-parent@~5.1.0:
version "5.1.2"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
dependencies:
is-glob "^4.0.1"
glob-to-regexp@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab"
@@ -7407,6 +7452,13 @@ is-binary-path@^1.0.0:
dependencies:
binary-extensions "^1.0.0"
is-binary-path@~2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
dependencies:
binary-extensions "^2.0.0"
is-buffer@^1.1.5:
version "1.1.6"
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
@@ -7505,7 +7557,7 @@ is-glob@^3.1.0:
dependencies:
is-extglob "^2.1.0"
is-glob@^4.0.0, is-glob@^4.0.1:
is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
@@ -7701,6 +7753,13 @@ js-yaml@^3.13.0, js-yaml@^3.13.1, js-yaml@^3.2.5, js-yaml@^3.2.7:
argparse "^1.0.7"
esprima "^4.0.0"
js-yaml@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.0.0.tgz#f426bc0ff4b4051926cd588c71113183409a121f"
integrity sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==
dependencies:
argparse "^2.0.1"
jsbn@~0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
@@ -8728,7 +8787,7 @@ normalize-path@^2.1.1:
dependencies:
remove-trailing-separator "^1.0.1"
normalize-path@^3.0.0:
normalize-path@^3.0.0, normalize-path@~3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
@@ -9193,7 +9252,7 @@ performance-now@^2.1.0:
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
picomatch@^2.0.5, picomatch@^2.2.1:
picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1:
version "2.2.2"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad"
integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==
@@ -9550,6 +9609,13 @@ readdirp@^2.2.1:
micromatch "^3.1.10"
readable-stream "^2.0.2"
readdirp@~3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e"
integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==
dependencies:
picomatch "^2.2.1"
recast@^0.18.1:
version "0.18.10"
resolved "https://registry.yarnpkg.com/recast/-/recast-0.18.10.tgz#605ebbe621511eb89b6356a7e224bff66ed91478"
@@ -9980,6 +10046,13 @@ sane@^4.0.0, sane@^4.1.0:
minimist "^1.1.1"
walker "~1.0.5"
sass@^1.32.8:
version "1.32.8"
resolved "https://registry.yarnpkg.com/sass/-/sass-1.32.8.tgz#f16a9abd8dc530add8834e506878a2808c037bdc"
integrity sha512-Sl6mIeGpzjIUZqvKnKETfMf0iDAswD9TNlv13A7aAF3XZlRPMq4VvJWBC2N2DXbp94MQVdNSFG6LfF/iOXrPHQ==
dependencies:
chokidar ">=2.0.0 <4.0.0"
saxes@^3.1.3:
version "3.1.11"
resolved "https://registry.yarnpkg.com/saxes/-/saxes-3.1.11.tgz#d59d1fd332ec92ad98a2e0b2ee644702384b1c5b"

View File

@@ -0,0 +1,20 @@
// styles for Ember CLI testem environment
:root {
--font-family: "Arial";
}
$primary: #222222 !default;
$secondary: #ffffff !default;
$tertiary: #0088cc !default;
$quaternary: #e45735 !default;
$header_background: #ffffff !default;
$header_primary: #333333 !default;
$highlight: #ffff4d !default;
$danger: #e45735 !default;
$success: #009900 !default;
$love: #fa6c8d !default;
@import "common/foundation/variables";
@import "common/foundation/mixins";
@import "desktop";