mirror of
https://github.com/pulsar-edit/pulsar.git
synced 2024-11-10 10:17:11 +03:00
ff429230f1
In Chrome 66+ it seems that getComputedStyle().fontWeight returns the computed numeric value of the style instead of the original descriptive name. We now look for value '700' which corresponds to the value of 'bold'.
504 lines
20 KiB
JavaScript
504 lines
20 KiB
JavaScript
const path = require('path')
|
|
const fs = require('fs-plus')
|
|
const temp = require('temp').track()
|
|
|
|
describe('atom.themes', function () {
|
|
beforeEach(function () {
|
|
spyOn(atom, 'inSpecMode').andReturn(false)
|
|
spyOn(console, 'warn')
|
|
})
|
|
|
|
afterEach(function () {
|
|
waitsForPromise(() => atom.themes.deactivateThemes())
|
|
runs(function () {
|
|
try {
|
|
temp.cleanupSync()
|
|
} catch (error) {}
|
|
})
|
|
})
|
|
|
|
describe('theme getters and setters', function () {
|
|
beforeEach(function () {
|
|
jasmine.snapshotDeprecations()
|
|
atom.packages.loadPackages()
|
|
})
|
|
|
|
afterEach(() => jasmine.restoreDeprecationsSnapshot())
|
|
|
|
describe('getLoadedThemes', () =>
|
|
it('gets all the loaded themes', function () {
|
|
const themes = atom.themes.getLoadedThemes()
|
|
expect(themes.length).toBeGreaterThan(2)
|
|
})
|
|
)
|
|
|
|
describe('getActiveThemes', () =>
|
|
it('gets all the active themes', function () {
|
|
waitsForPromise(() => atom.themes.activateThemes())
|
|
|
|
runs(function () {
|
|
const names = atom.config.get('core.themes')
|
|
expect(names.length).toBeGreaterThan(0)
|
|
const themes = atom.themes.getActiveThemes()
|
|
expect(themes).toHaveLength(names.length)
|
|
})
|
|
})
|
|
)
|
|
})
|
|
|
|
describe('when the core.themes config value contains invalid entry', () =>
|
|
it('ignores theme', function () {
|
|
atom.config.set('core.themes', [
|
|
'atom-light-ui',
|
|
null,
|
|
undefined,
|
|
'',
|
|
false,
|
|
4,
|
|
{},
|
|
[],
|
|
'atom-dark-ui'
|
|
])
|
|
|
|
expect(atom.themes.getEnabledThemeNames()).toEqual(['atom-dark-ui', 'atom-light-ui'])
|
|
})
|
|
)
|
|
|
|
describe('::getImportPaths()', function () {
|
|
it('returns the theme directories before the themes are loaded', function () {
|
|
atom.config.set('core.themes', ['theme-with-index-less', 'atom-dark-ui', 'atom-light-ui'])
|
|
|
|
const paths = atom.themes.getImportPaths()
|
|
|
|
// syntax theme is not a dir at this time, so only two.
|
|
expect(paths.length).toBe(2)
|
|
expect(paths[0]).toContain('atom-light-ui')
|
|
expect(paths[1]).toContain('atom-dark-ui')
|
|
})
|
|
|
|
it('ignores themes that cannot be resolved to a directory', function () {
|
|
atom.config.set('core.themes', ['definitely-not-a-theme'])
|
|
expect(() => atom.themes.getImportPaths()).not.toThrow()
|
|
})
|
|
})
|
|
|
|
describe('when the core.themes config value changes', function () {
|
|
it('add/removes stylesheets to reflect the new config value', function () {
|
|
let didChangeActiveThemesHandler
|
|
atom.themes.onDidChangeActiveThemes(didChangeActiveThemesHandler = jasmine.createSpy())
|
|
spyOn(atom.styles, 'getUserStyleSheetPath').andCallFake(() => null)
|
|
|
|
waitsForPromise(() => atom.themes.activateThemes())
|
|
|
|
runs(function () {
|
|
didChangeActiveThemesHandler.reset()
|
|
atom.config.set('core.themes', [])
|
|
})
|
|
|
|
waitsFor('a', () => didChangeActiveThemesHandler.callCount === 1)
|
|
|
|
runs(function () {
|
|
didChangeActiveThemesHandler.reset()
|
|
expect(document.querySelectorAll('style.theme')).toHaveLength(0)
|
|
atom.config.set('core.themes', ['atom-dark-ui'])
|
|
})
|
|
|
|
waitsFor('b', () => didChangeActiveThemesHandler.callCount === 1)
|
|
|
|
runs(function () {
|
|
didChangeActiveThemesHandler.reset()
|
|
expect(document.querySelectorAll('style[priority="1"]')).toHaveLength(2)
|
|
expect(document.querySelector('style[priority="1"]').getAttribute('source-path')).toMatch(/atom-dark-ui/)
|
|
atom.config.set('core.themes', ['atom-light-ui', 'atom-dark-ui'])
|
|
})
|
|
|
|
waitsFor('c', () => didChangeActiveThemesHandler.callCount === 1)
|
|
|
|
runs(function () {
|
|
didChangeActiveThemesHandler.reset()
|
|
expect(document.querySelectorAll('style[priority="1"]')).toHaveLength(2)
|
|
expect(document.querySelectorAll('style[priority="1"]')[0].getAttribute('source-path')).toMatch(/atom-dark-ui/)
|
|
expect(document.querySelectorAll('style[priority="1"]')[1].getAttribute('source-path')).toMatch(/atom-light-ui/)
|
|
atom.config.set('core.themes', [])
|
|
})
|
|
|
|
waitsFor(() => didChangeActiveThemesHandler.callCount === 1)
|
|
|
|
runs(function () {
|
|
didChangeActiveThemesHandler.reset()
|
|
expect(document.querySelectorAll('style[priority="1"]')).toHaveLength(2)
|
|
// atom-dark-ui has a directory path, the syntax one doesn't
|
|
atom.config.set('core.themes', ['theme-with-index-less', 'atom-dark-ui'])
|
|
})
|
|
|
|
waitsFor(() => didChangeActiveThemesHandler.callCount === 1)
|
|
|
|
runs(function () {
|
|
expect(document.querySelectorAll('style[priority="1"]')).toHaveLength(2)
|
|
const importPaths = atom.themes.getImportPaths()
|
|
expect(importPaths.length).toBe(1)
|
|
expect(importPaths[0]).toContain('atom-dark-ui')
|
|
})
|
|
})
|
|
|
|
it('adds theme-* classes to the workspace for each active theme', function () {
|
|
atom.config.set('core.themes', ['atom-dark-ui', 'atom-dark-syntax'])
|
|
|
|
let didChangeActiveThemesHandler
|
|
atom.themes.onDidChangeActiveThemes(didChangeActiveThemesHandler = jasmine.createSpy())
|
|
waitsForPromise(() => atom.themes.activateThemes())
|
|
|
|
const workspaceElement = atom.workspace.getElement()
|
|
runs(function () {
|
|
expect(workspaceElement).toHaveClass('theme-atom-dark-ui')
|
|
|
|
atom.themes.onDidChangeActiveThemes(didChangeActiveThemesHandler = jasmine.createSpy())
|
|
atom.config.set('core.themes', ['theme-with-ui-variables', 'theme-with-syntax-variables'])
|
|
})
|
|
|
|
waitsFor(() => didChangeActiveThemesHandler.callCount > 0)
|
|
|
|
runs(function () {
|
|
// `theme-` twice as it prefixes the name with `theme-`
|
|
expect(workspaceElement).toHaveClass('theme-theme-with-ui-variables')
|
|
expect(workspaceElement).toHaveClass('theme-theme-with-syntax-variables')
|
|
expect(workspaceElement).not.toHaveClass('theme-atom-dark-ui')
|
|
expect(workspaceElement).not.toHaveClass('theme-atom-dark-syntax')
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('when a theme fails to load', () =>
|
|
it('logs a warning', function () {
|
|
console.warn.reset()
|
|
atom.packages.activatePackage('a-theme-that-will-not-be-found').then(function () {}, function () {})
|
|
expect(console.warn.callCount).toBe(1)
|
|
expect(console.warn.argsForCall[0][0]).toContain("Could not resolve 'a-theme-that-will-not-be-found'")
|
|
})
|
|
)
|
|
|
|
describe('::requireStylesheet(path)', function () {
|
|
beforeEach(() => jasmine.snapshotDeprecations())
|
|
|
|
afterEach(() => jasmine.restoreDeprecationsSnapshot())
|
|
|
|
it('synchronously loads css at the given path and installs a style tag for it in the head', function () {
|
|
let styleElementAddedHandler
|
|
atom.styles.onDidAddStyleElement(styleElementAddedHandler = jasmine.createSpy('styleElementAddedHandler'))
|
|
|
|
const cssPath = getAbsolutePath(atom.project.getDirectories()[0], 'css.css')
|
|
const lengthBefore = document.querySelectorAll('head style').length
|
|
|
|
atom.themes.requireStylesheet(cssPath)
|
|
expect(document.querySelectorAll('head style').length).toBe(lengthBefore + 1)
|
|
|
|
expect(styleElementAddedHandler).toHaveBeenCalled()
|
|
|
|
const element = document.querySelector('head style[source-path*="css.css"]')
|
|
expect(element.getAttribute('source-path')).toEqualPath(cssPath)
|
|
expect(element.textContent).toBe(fs.readFileSync(cssPath, 'utf8'))
|
|
|
|
// doesn't append twice
|
|
styleElementAddedHandler.reset()
|
|
atom.themes.requireStylesheet(cssPath)
|
|
expect(document.querySelectorAll('head style').length).toBe(lengthBefore + 1)
|
|
expect(styleElementAddedHandler).not.toHaveBeenCalled()
|
|
|
|
document.querySelectorAll('head style[id*="css.css"]').forEach((styleElement) => {
|
|
styleElement.remove()
|
|
})
|
|
})
|
|
|
|
it('synchronously loads and parses less files at the given path and installs a style tag for it in the head', function () {
|
|
const lessPath = getAbsolutePath(atom.project.getDirectories()[0], 'sample.less')
|
|
const lengthBefore = document.querySelectorAll('head style').length
|
|
atom.themes.requireStylesheet(lessPath)
|
|
expect(document.querySelectorAll('head style').length).toBe(lengthBefore + 1)
|
|
|
|
const element = document.querySelector('head style[source-path*="sample.less"]')
|
|
expect(element.getAttribute('source-path')).toEqualPath(lessPath)
|
|
expect(element.textContent.toLowerCase()).toBe(`\
|
|
#header {
|
|
color: #4d926f;
|
|
}
|
|
h2 {
|
|
color: #4d926f;
|
|
}
|
|
\
|
|
`
|
|
)
|
|
|
|
// doesn't append twice
|
|
atom.themes.requireStylesheet(lessPath)
|
|
expect(document.querySelectorAll('head style').length).toBe(lengthBefore + 1)
|
|
document.querySelectorAll('head style[id*="sample.less"]').forEach((styleElement) => {
|
|
styleElement.remove()
|
|
})
|
|
})
|
|
|
|
it('supports requiring css and less stylesheets without an explicit extension', function () {
|
|
atom.themes.requireStylesheet(path.join(__dirname, 'fixtures', 'css'))
|
|
expect(document.querySelector('head style[source-path*="css.css"]').getAttribute('source-path'))
|
|
.toEqualPath(getAbsolutePath(atom.project.getDirectories()[0], 'css.css'))
|
|
atom.themes.requireStylesheet(path.join(__dirname, 'fixtures', 'sample'))
|
|
expect(document.querySelector('head style[source-path*="sample.less"]').getAttribute('source-path'))
|
|
.toEqualPath(getAbsolutePath(atom.project.getDirectories()[0], 'sample.less'))
|
|
|
|
document.querySelector('head style[source-path*="css.css"]').remove()
|
|
document.querySelector('head style[source-path*="sample.less"]').remove()
|
|
})
|
|
|
|
it('returns a disposable allowing styles applied by the given path to be removed', function () {
|
|
const cssPath = require.resolve('./fixtures/css.css')
|
|
|
|
expect(getComputedStyle(document.body).fontWeight).not.toBe('700')
|
|
const disposable = atom.themes.requireStylesheet(cssPath)
|
|
expect(getComputedStyle(document.body).fontWeight).toBe('700')
|
|
|
|
let styleElementRemovedHandler
|
|
atom.styles.onDidRemoveStyleElement(styleElementRemovedHandler = jasmine.createSpy('styleElementRemovedHandler'))
|
|
|
|
disposable.dispose()
|
|
|
|
expect(getComputedStyle(document.body).fontWeight).not.toBe('bold')
|
|
|
|
expect(styleElementRemovedHandler).toHaveBeenCalled()
|
|
})
|
|
})
|
|
|
|
describe('base style sheet loading', function () {
|
|
beforeEach(function () {
|
|
const workspaceElement = atom.workspace.getElement()
|
|
jasmine.attachToDOM(atom.workspace.getElement())
|
|
workspaceElement.appendChild(document.createElement('atom-text-editor'))
|
|
|
|
waitsForPromise(() => atom.themes.activateThemes())
|
|
})
|
|
|
|
it("loads the correct values from the theme's ui-variables file", function () {
|
|
let didChangeActiveThemesHandler
|
|
atom.themes.onDidChangeActiveThemes(didChangeActiveThemesHandler = jasmine.createSpy())
|
|
atom.config.set('core.themes', ['theme-with-ui-variables', 'theme-with-syntax-variables'])
|
|
|
|
waitsFor(() => didChangeActiveThemesHandler.callCount > 0)
|
|
|
|
runs(function () {
|
|
// an override loaded in the base css
|
|
expect(getComputedStyle(atom.workspace.getElement())['background-color']).toBe('rgb(0, 0, 255)')
|
|
|
|
// from within the theme itself
|
|
expect(getComputedStyle(document.querySelector('atom-text-editor')).paddingTop).toBe('150px')
|
|
expect(getComputedStyle(document.querySelector('atom-text-editor')).paddingRight).toBe('150px')
|
|
expect(getComputedStyle(document.querySelector('atom-text-editor')).paddingBottom).toBe('150px')
|
|
})
|
|
})
|
|
|
|
describe('when there is a theme with incomplete variables', () =>
|
|
it('loads the correct values from the fallback ui-variables', function () {
|
|
let didChangeActiveThemesHandler
|
|
atom.themes.onDidChangeActiveThemes(didChangeActiveThemesHandler = jasmine.createSpy())
|
|
atom.config.set('core.themes', ['theme-with-incomplete-ui-variables', 'theme-with-syntax-variables'])
|
|
|
|
waitsFor(() => didChangeActiveThemesHandler.callCount > 0)
|
|
|
|
runs(function () {
|
|
// an override loaded in the base css
|
|
expect(getComputedStyle(atom.workspace.getElement())['background-color']).toBe('rgb(0, 0, 255)')
|
|
|
|
// from within the theme itself
|
|
expect(getComputedStyle(document.querySelector('atom-text-editor')).backgroundColor).toBe('rgb(0, 152, 255)')
|
|
})
|
|
})
|
|
)
|
|
})
|
|
|
|
describe('user stylesheet', function () {
|
|
let userStylesheetPath
|
|
beforeEach(function () {
|
|
userStylesheetPath = path.join(temp.mkdirSync('atom'), 'styles.less')
|
|
fs.writeFileSync(userStylesheetPath, 'body {border-style: dotted !important;}')
|
|
spyOn(atom.styles, 'getUserStyleSheetPath').andReturn(userStylesheetPath)
|
|
})
|
|
|
|
describe('when the user stylesheet changes', function () {
|
|
beforeEach(() => jasmine.snapshotDeprecations())
|
|
|
|
afterEach(() => jasmine.restoreDeprecationsSnapshot())
|
|
|
|
it('reloads it', function () {
|
|
let styleElementAddedHandler, styleElementRemovedHandler
|
|
|
|
waitsForPromise(() => atom.themes.activateThemes())
|
|
|
|
runs(function () {
|
|
atom.styles.onDidRemoveStyleElement(styleElementRemovedHandler = jasmine.createSpy('styleElementRemovedHandler'))
|
|
atom.styles.onDidAddStyleElement(styleElementAddedHandler = jasmine.createSpy('styleElementAddedHandler'))
|
|
|
|
spyOn(atom.themes, 'loadUserStylesheet').andCallThrough()
|
|
|
|
expect(getComputedStyle(document.body).borderStyle).toBe('dotted')
|
|
fs.writeFileSync(userStylesheetPath, 'body {border-style: dashed}')
|
|
})
|
|
|
|
waitsFor(() => atom.themes.loadUserStylesheet.callCount === 1)
|
|
|
|
runs(function () {
|
|
expect(getComputedStyle(document.body).borderStyle).toBe('dashed')
|
|
|
|
expect(styleElementRemovedHandler).toHaveBeenCalled()
|
|
expect(styleElementRemovedHandler.argsForCall[0][0].textContent).toContain('dotted')
|
|
|
|
expect(styleElementAddedHandler).toHaveBeenCalled()
|
|
expect(styleElementAddedHandler.argsForCall[0][0].textContent).toContain('dashed')
|
|
|
|
styleElementRemovedHandler.reset()
|
|
fs.removeSync(userStylesheetPath)
|
|
})
|
|
|
|
waitsFor(() => atom.themes.loadUserStylesheet.callCount === 2)
|
|
|
|
runs(function () {
|
|
expect(styleElementRemovedHandler).toHaveBeenCalled()
|
|
expect(styleElementRemovedHandler.argsForCall[0][0].textContent).toContain('dashed')
|
|
expect(getComputedStyle(document.body).borderStyle).toBe('none')
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('when there is an error reading the stylesheet', function () {
|
|
let addErrorHandler = null
|
|
beforeEach(function () {
|
|
atom.themes.loadUserStylesheet()
|
|
spyOn(atom.themes.lessCache, 'cssForFile').andCallFake(function () {
|
|
throw new Error('EACCES permission denied "styles.less"')
|
|
})
|
|
atom.notifications.onDidAddNotification(addErrorHandler = jasmine.createSpy())
|
|
})
|
|
|
|
it('creates an error notification and does not add the stylesheet', function () {
|
|
atom.themes.loadUserStylesheet()
|
|
expect(addErrorHandler).toHaveBeenCalled()
|
|
const note = addErrorHandler.mostRecentCall.args[0]
|
|
expect(note.getType()).toBe('error')
|
|
expect(note.getMessage()).toContain('Error loading')
|
|
expect(atom.styles.styleElementsBySourcePath[atom.styles.getUserStyleSheetPath()]).toBeUndefined()
|
|
})
|
|
})
|
|
|
|
describe('when there is an error watching the user stylesheet', function () {
|
|
let addErrorHandler = null
|
|
beforeEach(function () {
|
|
const {File} = require('pathwatcher')
|
|
spyOn(File.prototype, 'on').andCallFake(function (event) {
|
|
if (event.indexOf('contents-changed') > -1) {
|
|
throw new Error('Unable to watch path')
|
|
}
|
|
})
|
|
spyOn(atom.themes, 'loadStylesheet').andReturn('')
|
|
atom.notifications.onDidAddNotification(addErrorHandler = jasmine.createSpy())
|
|
})
|
|
|
|
it('creates an error notification', function () {
|
|
atom.themes.loadUserStylesheet()
|
|
expect(addErrorHandler).toHaveBeenCalled()
|
|
const note = addErrorHandler.mostRecentCall.args[0]
|
|
expect(note.getType()).toBe('error')
|
|
expect(note.getMessage()).toContain('Unable to watch path')
|
|
})
|
|
})
|
|
|
|
it("adds a notification when a theme's stylesheet is invalid", function () {
|
|
const addErrorHandler = jasmine.createSpy()
|
|
atom.notifications.onDidAddNotification(addErrorHandler)
|
|
expect(() => atom.packages.activatePackage('theme-with-invalid-styles').then(function () {}, function () {})).not.toThrow()
|
|
expect(addErrorHandler.callCount).toBe(2)
|
|
expect(addErrorHandler.argsForCall[1][0].message).toContain('Failed to activate the theme-with-invalid-styles theme')
|
|
})
|
|
})
|
|
|
|
describe('when a non-existent theme is present in the config', function () {
|
|
beforeEach(function () {
|
|
console.warn.reset()
|
|
atom.config.set('core.themes', ['non-existent-dark-ui', 'non-existent-dark-syntax'])
|
|
|
|
waitsForPromise(() => atom.themes.activateThemes())
|
|
})
|
|
|
|
it('uses the default one-dark UI and syntax themes and logs a warning', function () {
|
|
const activeThemeNames = atom.themes.getActiveThemeNames()
|
|
expect(console.warn.callCount).toBe(2)
|
|
expect(activeThemeNames.length).toBe(2)
|
|
expect(activeThemeNames).toContain('one-dark-ui')
|
|
expect(activeThemeNames).toContain('one-dark-syntax')
|
|
})
|
|
})
|
|
|
|
describe('when in safe mode', function () {
|
|
describe('when the enabled UI and syntax themes are bundled with Atom', function () {
|
|
beforeEach(function () {
|
|
atom.config.set('core.themes', ['atom-light-ui', 'atom-dark-syntax'])
|
|
|
|
waitsForPromise(() => atom.themes.activateThemes())
|
|
})
|
|
|
|
it('uses the enabled themes', function () {
|
|
const activeThemeNames = atom.themes.getActiveThemeNames()
|
|
expect(activeThemeNames.length).toBe(2)
|
|
expect(activeThemeNames).toContain('atom-light-ui')
|
|
expect(activeThemeNames).toContain('atom-dark-syntax')
|
|
})
|
|
})
|
|
|
|
describe('when the enabled UI and syntax themes are not bundled with Atom', function () {
|
|
beforeEach(function () {
|
|
atom.config.set('core.themes', ['installed-dark-ui', 'installed-dark-syntax'])
|
|
|
|
waitsForPromise(() => atom.themes.activateThemes())
|
|
})
|
|
|
|
it('uses the default dark UI and syntax themes', function () {
|
|
const activeThemeNames = atom.themes.getActiveThemeNames()
|
|
expect(activeThemeNames.length).toBe(2)
|
|
expect(activeThemeNames).toContain('one-dark-ui')
|
|
expect(activeThemeNames).toContain('one-dark-syntax')
|
|
})
|
|
})
|
|
|
|
describe('when the enabled UI theme is not bundled with Atom', function () {
|
|
beforeEach(function () {
|
|
atom.config.set('core.themes', ['installed-dark-ui', 'atom-light-syntax'])
|
|
|
|
waitsForPromise(() => atom.themes.activateThemes())
|
|
})
|
|
|
|
it('uses the default one-dark UI theme', function () {
|
|
const activeThemeNames = atom.themes.getActiveThemeNames()
|
|
expect(activeThemeNames.length).toBe(2)
|
|
expect(activeThemeNames).toContain('one-dark-ui')
|
|
expect(activeThemeNames).toContain('atom-light-syntax')
|
|
})
|
|
})
|
|
|
|
describe('when the enabled syntax theme is not bundled with Atom', function () {
|
|
beforeEach(function () {
|
|
atom.config.set('core.themes', ['atom-light-ui', 'installed-dark-syntax'])
|
|
|
|
waitsForPromise(() => atom.themes.activateThemes())
|
|
})
|
|
|
|
it('uses the default one-dark syntax theme', function () {
|
|
const activeThemeNames = atom.themes.getActiveThemeNames()
|
|
expect(activeThemeNames.length).toBe(2)
|
|
expect(activeThemeNames).toContain('atom-light-ui')
|
|
expect(activeThemeNames).toContain('one-dark-syntax')
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
function getAbsolutePath (directory, relativePath) {
|
|
if (directory) {
|
|
return directory.resolve(relativePath)
|
|
}
|
|
}
|