Validate whether profiles marked as incomplete are actually incomplete (#443)

This commit is contained in:
James Nylen 2018-06-24 21:42:33 -05:00 committed by GitHub
parent 788c25c8ab
commit cbd3073fd6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 488 additions and 48 deletions

View file

@ -152,7 +152,7 @@ Name | Website | Region
[GigSalad](/company-profiles/gigsalad.md) | https://www.gigsalad.com/ | US [GigSalad](/company-profiles/gigsalad.md) | https://www.gigsalad.com/ | US
[Gitbook](/company-profiles/gitbook.md) ⚠️️ | https://www.gitbook.com/ | [Gitbook](/company-profiles/gitbook.md) ⚠️️ | https://www.gitbook.com/ |
[GitHub](/company-profiles/github.md) | https://github.com/ | Worldwide [GitHub](/company-profiles/github.md) | https://github.com/ | Worldwide
[GitLab](/company-profiles/gitlab.md) ⚠️️ | https://about.gitlab.com/ | Worldwide [GitLab](/company-profiles/gitlab.md) | https://about.gitlab.com/ | Worldwide
[GitPrime](/company-profiles/gitprime.md) | https://gitprime.com/ | Worldwide [GitPrime](/company-profiles/gitprime.md) | https://gitprime.com/ | Worldwide
[Glue Networks](/company-profiles/glue-networks.md) ⚠️️ | http://gluenetworks.com/ | [Glue Networks](/company-profiles/glue-networks.md) ⚠️️ | http://gluenetworks.com/ |
[GoHiring](/company-profiles/gohiring.md) ⚠️️ | http://www.gohiring.com/ | Worldwide [GoHiring](/company-profiles/gohiring.md) ⚠️️ | http://www.gohiring.com/ | Worldwide

View file

@ -2,6 +2,7 @@
const fs = require( 'fs' ); const fs = require( 'fs' );
const path = require( 'path' ); const path = require( 'path' );
const util = require( 'util' );
const cheerio = require( 'cheerio' ); const cheerio = require( 'cheerio' );
const marked = require( 'marked' ); const marked = require( 'marked' );
@ -39,6 +40,14 @@ const headingsOptional = [
* Utility functions * Utility functions
*/ */
function error( filename, msg, ...params ) {
errorCount++;
const msgFormatted = util.format( msg, ...params );
msgFormatted.split( '\n' ).forEach( line => {
console.log( '%s: %s', filename, line );
} );
}
function companyNameToProfileFilename( companyName ) { function companyNameToProfileFilename( companyName ) {
return companyName.toLowerCase() return companyName.toLowerCase()
.replace( /&/g, ' and ' ) .replace( /&/g, ' and ' )
@ -47,6 +56,13 @@ function companyNameToProfileFilename( companyName ) {
.replace( /^-|-$/g, '' ); .replace( /^-|-$/g, '' );
} }
// adapted from https://gist.github.com/RandomEtc/2657669
function jsonStringifyUnicodeEscaped( obj ) {
return JSON.stringify( obj ).replace( /[\u007f-\uffff]/g, c => {
return '\\u' + ( '0000' + c.charCodeAt( 0 ).toString( 16 ) ).slice( -4 );
} );
}
/** /**
* Build list of Markdown files containing company profiles. * Build list of Markdown files containing company profiles.
@ -70,11 +86,7 @@ const readmeMarkdown = fs.readFileSync(
const $ = cheerio.load( marked( readmeMarkdown ) ); const $ = cheerio.load( marked( readmeMarkdown ) );
function readmeError( msg, ...params ) { function readmeError( msg, ...params ) {
errorCount++; error( 'README.md', msg, ...params );
console.log(
'README.md: ' + msg,
...params
);
} }
let lastCompanyName = null; let lastCompanyName = null;
@ -93,13 +105,17 @@ $( 'tr' ).each( ( i, tr ) => {
); );
} }
const entry = { const readmeEntry = {
name: $td.eq( 0 ).text().replace( '\u26a0', '' ).trim(), // Strip out warning emoji indicating that this profile is incomplete,
// and any following unicode chars
name: $td.eq( 0 ).text().replace( /\u26a0\ufe0f*/, '' ).trim(),
// Detect warning emoji next to company name
isIncomplete: /\u26a0/.test( $td.eq( 0 ).text() ),
website: $td.eq( 1 ).text(), website: $td.eq( 1 ).text(),
shortRegion: $td.eq( 2 ).text(), shortRegion: $td.eq( 2 ).text(),
}; };
if ( ! entry.name ) { if ( ! readmeEntry.name ) {
readmeError( readmeError(
'Missing company name: %s', 'Missing company name: %s',
$( tr ).html().replace( /\n/g, '' ) $( tr ).html().replace( /\n/g, '' )
@ -108,46 +124,55 @@ $( 'tr' ).each( ( i, tr ) => {
if ( if (
lastCompanyName && lastCompanyName &&
entry.name.toLowerCase() < lastCompanyName.toLowerCase() readmeEntry.name.toLowerCase() < lastCompanyName.toLowerCase()
) { ) {
readmeError( readmeError(
'Company is listed out of order: "%s" (should be before "%s")', 'Company is listed out of order: "%s" (should be before "%s")',
entry.name, readmeEntry.name,
lastCompanyName lastCompanyName
); );
} }
lastCompanyName = entry.name; lastCompanyName = readmeEntry.name;
const profileLink = $td.eq( 0 ).find( 'a' ).attr( 'href' ); const $profileLink = $td.eq( 0 ).find( 'a' );
if ( profileLink ) { if ( $profileLink.length === 1 ) {
const match = profileLink.match( /^\/company-profiles\/(.*\.md)$/ ); const match = $profileLink.attr( 'href' ).match( /^\/company-profiles\/(.*\.md)$/ );
if ( match ) { if ( match ) {
entry.linkedFilename = match[ 1 ]; readmeEntry.linkedFilename = match[ 1 ];
if ( profileFilenames.indexOf( entry.linkedFilename ) === -1 ) { if ( profileFilenames.indexOf( readmeEntry.linkedFilename ) === -1 ) {
readmeError( readmeError(
'Broken link to company "%s": "%s"', 'Broken link to company "%s": "%s"',
entry.name, readmeEntry.name,
profileLink $profileLink.attr( 'href' )
);
}
const nameCheck = $profileLink.text().trim();
if ( nameCheck !== readmeEntry.name ) {
readmeError(
'Extra text in company name: %s, %s',
jsonStringifyUnicodeEscaped( nameCheck ),
jsonStringifyUnicodeEscaped( readmeEntry.name )
); );
} }
} else { } else {
readmeError( readmeError(
'Invalid link to company "%s": "%s"', 'Invalid link to company "%s": "%s"',
entry.name, readmeEntry.name,
profileLink $profileLink.attr( 'href' )
); );
} }
} else { } else {
readmeError( readmeError(
'Company "%s" has no linked Markdown profile ("%s.md")', 'Company "%s" has no linked Markdown profile ("%s.md")',
entry.name, readmeEntry.name,
companyNameToProfileFilename( entry.name ) companyNameToProfileFilename( readmeEntry.name )
); );
} }
readmeCompanies.push( entry ); readmeCompanies.push( readmeEntry );
} ); } );
@ -158,12 +183,8 @@ $( 'tr' ).each( ( i, tr ) => {
const allProfileHeadings = {}; const allProfileHeadings = {};
profileFilenames.forEach( filename => { profileFilenames.forEach( filename => {
function error( msg, ...params ) { function profileError( msg, ...params ) {
errorCount++; error( filename, msg, ...params );
console.log(
filename + ': ' + msg,
...params
);
} }
const profileMarkdown = fs.readFileSync( const profileMarkdown = fs.readFileSync(
@ -175,7 +196,7 @@ profileFilenames.forEach( filename => {
let hasTitleError = false; let hasTitleError = false;
if ( $( 'h1' ).length !== 1 ) { if ( $( 'h1' ).length !== 1 ) {
error( profileError(
'Expected 1 first-level heading but found %d', 'Expected 1 first-level heading but found %d',
$( 'h1' ).length $( 'h1' ).length
); );
@ -183,7 +204,7 @@ profileFilenames.forEach( filename => {
} }
if ( ! $( 'h1' ).parent().is( 'body' ) ) { if ( ! $( 'h1' ).parent().is( 'body' ) ) {
error( profileError(
'The main title is wrapped inside of another element.' 'The main title is wrapped inside of another element.'
); );
} }
@ -191,7 +212,7 @@ profileFilenames.forEach( filename => {
const companyName = $( 'h1' ).text(); const companyName = $( 'h1' ).text();
if ( ! /[a-z]/i.test( companyName ) ) { if ( ! /[a-z]/i.test( companyName ) ) {
error( profileError(
'Company name looks wrong: "%s"', 'Company name looks wrong: "%s"',
companyName companyName
); );
@ -207,18 +228,19 @@ profileFilenames.forEach( filename => {
// which is fine. // which is fine.
filenameExpected.substring( 0, filenameBase.length + 1 ) !== filenameBase + '-' filenameExpected.substring( 0, filenameBase.length + 1 ) !== filenameBase + '-'
) { ) {
error( profileError(
'Expected filename "%s.md" for company "%s"', 'Expected filename "%s.md" for company "%s"',
filenameExpected, filenameExpected,
companyName companyName
); );
} }
if ( const readmeEntry = readmeCompanies.find(
filename !== 'example.md' && readmeEntry => readmeEntry.linkedFilename === filename
! readmeCompanies.some( entry => entry.linkedFilename === filename ) );
) {
error( 'No link to company profile from readme' ); if ( filename !== 'example.md' && ! readmeEntry ) {
profileError( 'No link to company profile from readme' );
} }
// Build and validate list of headings contained in this Markdown profile. // Build and validate list of headings contained in this Markdown profile.
@ -229,14 +251,14 @@ profileFilenames.forEach( filename => {
const headingName = $( el ).html(); const headingName = $( el ).html();
if ( ! $( el ).parent().is( 'body' ) ) { if ( ! $( el ).parent().is( 'body' ) ) {
error( profileError(
'The section heading for "%s" is wrapped inside of another element.', 'The section heading for "%s" is wrapped inside of another element.',
headingName headingName
); );
} }
if ( profileHeadings.indexOf( headingName ) >= 0 ) { if ( profileHeadings.indexOf( headingName ) >= 0 ) {
error( profileError(
'Duplicate section: "%s".', 'Duplicate section: "%s".',
headingName headingName
); );
@ -246,7 +268,7 @@ profileFilenames.forEach( filename => {
headingsRequired.indexOf( headingName ) === -1 && headingsRequired.indexOf( headingName ) === -1 &&
headingsOptional.indexOf( headingName ) === -1 headingsOptional.indexOf( headingName ) === -1
) { ) {
error( profileError(
'Invalid section: "%s". Expected one of: %s', 'Invalid section: "%s". Expected one of: %s',
headingName, headingName,
JSON.stringify( headingsRequired.concat( headingsOptional ) ) JSON.stringify( headingsRequired.concat( headingsOptional ) )
@ -265,7 +287,7 @@ profileFilenames.forEach( filename => {
headingsRequired.forEach( headingName => { headingsRequired.forEach( headingName => {
if ( profileHeadings.indexOf( headingName ) === -1 ) { if ( profileHeadings.indexOf( headingName ) === -1 ) {
error( profileError(
'Required section "%s" not found.', 'Required section "%s" not found.',
headingName headingName
); );
@ -293,7 +315,7 @@ profileFilenames.forEach( filename => {
+ '\n' + $.html( el ) + '\n' + $.html( el )
).trim(); ).trim();
} else { } else {
error( profileError(
'Content is not part of any section: %s', 'Content is not part of any section: %s',
$.html( el ).replace( /\n/g, '' ) $.html( el ).replace( /\n/g, '' )
); );
@ -305,12 +327,60 @@ profileFilenames.forEach( filename => {
.replace( /<[^>]+>/g, '' ) .replace( /<[^>]+>/g, '' )
.trim(); .trim();
if ( ! sectionText ) { if ( ! sectionText ) {
error( profileError(
'Empty section: "%s". Leave it out instead.', 'Empty section: "%s". Leave it out instead.',
heading heading
); );
} }
} ); } );
if ( readmeEntry ) {
// Check for company profiles that were filled in, but the "incomplete"
// mark was left in the readme, or vice versa.
const isIncomplete = {
readme: readmeEntry.isIncomplete,
sections: (
profileHeadings.length === 1 &&
profileHeadings[ 0 ] === 'Company blurb'
),
content: /&#x26A0;/.test( profileContent[ 'Company blurb' ] ),
};
const incompleteCount = Object.values( isIncomplete )
.reduce( ( sum, v ) => sum + ( v ? 1 : 0 ), 0 );
// incompleteCount === 0: Profile is incomplete; all 3 indicators are consistent
// incompleteCount === 3: Profile is "complete"; all 3 indicators are consistent
if ( incompleteCount === 1 ) {
if ( isIncomplete.readme ) {
profileError(
'Profile looks complete, but the main readme contains a warning emoji.'
);
} else if ( isIncomplete.sections ) {
profileError(
'Profile is marked as complete, but it only contains a "Company blurb" heading.'
)
} else { // isIncomplete.content
profileError(
'Profile looks complete, but the "Company blurb" contains a warning emoji.'
);
}
} else if ( incompleteCount === 2 ) {
if ( ! isIncomplete.readme ) {
profileError(
'Profile looks incomplete, but the main readme does not contain a warning emoji.'
);
} else if ( ! isIncomplete.sections ) {
profileError(
'Profile is marked as incomplete, but it contains multiple sections.'
+ '\nPlease remove the warning emoji from the "Company blurb" section and the main readme.'
)
} else { // ! isIncomplete.content
profileError(
'Profile looks incomplete, but the "Company blurb" does not contain a warning emoji.'
);
}
}
}
} ); } );
if ( process.env.REPORT_PROFILE_HEADINGS ) { if ( process.env.REPORT_PROFILE_HEADINGS ) {

View file

@ -0,0 +1,17 @@
# Test data
This company table and its linked company profiles contain incomplete and
complete profiles with various mismatching indicators about whether they are
complete (⚠ symbols, number of sections in the profile).
## Companies
Name | Website | Region
------------ | ------- | -------
[&yet](/company-profiles/and-yet.md) ⚠ | https://andyet.com | Worldwide
[10up](/company-profiles/10up.md) | https://10up.com/ | Worldwide
[17hats](/company-profiles/17hats.md) | https://www.17hats.com/ | Worldwide
[18F](/company-profiles/18f.md) | https://18f.gsa.gov/ | USA
[45royale](/company-profiles/45royale.md) ⚠ | http://45royale.com/ |
[Aerolab](/company-profiles/aerolab.md) ⚠ | https://aerolab.co/ |
[AngularClass](/company-profiles/angularclass.md) ⚠️ | https://angularclass.com | PST Timezone

View file

@ -0,0 +1,9 @@
# 10up, A WordPress Development Agency
## Company blurb
We make websites and content management simple and fun with premiere web design & development consulting services, by contributing to open platforms like WordPress, and by providing tools and products (like [PushUp](https://pushupnotifications.com/)) that make web publishing a cinch.
At 10up, we dont just “make” things we engineer them. Were a group of people built to solve problems; made to create; wired to delight. From beautiful pixels to beautiful code, we constantly improve the things around us, applying our passions to our clients projects and goals.
Weve had the privilege of working on big web projects for clients as diverse as TechCrunch, ESPNs FiveThirtyEight and Grantland, SurveyMonkey, Junior Diabetes Research Foundation (JDRF), and Google.

View file

@ -0,0 +1,23 @@
# 17hats
## Company blurb
Are you ready to stop juggling multiple apps to manage your business? From booking clients, to managing projects, to controlling your finances, with 17hats you have everything you need to manage your business and clients anytime, anywhere. Pretty neat if we say so ourselves! We created 17hats specifically for “businesses of one” with a focus on simplicity and ease of use.
⚠ Incomplete profile indicator
## Company size
11-50
## Remote status
Every engineer on our team works remotely. That being said, we do need someone who can work California business hours (9am-6pm PST) and who is available for emergencies at night. If you live in the LA area, you are welcome to be in our cool office in Pasadena. Bonus points if you can curse in Dutch.
## Region
Worldwide
## Company technologies
iOS, React, Knockout, Rails, Perl, HTML, Sql, Ruby, JQuery

View file

@ -0,0 +1,5 @@
# 18F
## Company blurb
⚠ We don't have much information about this company yet!

View file

@ -0,0 +1,21 @@
# 45royale
## Company blurb
⚠ We don't have much information about this company yet!
If you know something we don't, help us fill it in! Here's how:
- Read our [Contributing Guidelines](https://github.com/remoteintech/remote-jobs/blob/master/CONTRIBUTING.md)
- Have a look at our [example company profile](https://github.com/remoteintech/remote-jobs/blob/master/company-profiles/example.md)
- Follow the structure of the example profile and [send us a pull request with your changes to this file!](https://github.com/remoteintech/remote-jobs/edit/master/company-profiles/45royale.md)
## Company size
Something to tell the validation script that this profile has more information
than the standard "incomplete profile"
## Remote status
Something else to tell the validation script that this profile has more
information than the standard "incomplete profile"

View file

@ -0,0 +1,12 @@
# Aerolab
## Company blurb
We don't have much information about this company yet! (But the warning emoji
is missing from this paragraph)
If you know something we don't, help us fill it in! Here's how:
- Read our [Contributing Guidelines](https://github.com/remoteintech/remote-jobs/blob/master/CONTRIBUTING.md)
- Have a look at our [example company profile](https://github.com/remoteintech/remote-jobs/blob/master/company-profiles/example.md)
- Follow the structure of the example profile and [send us a pull request with your changes to this file!](https://github.com/remoteintech/remote-jobs/edit/master/company-profiles/aerolab.md)

View file

@ -0,0 +1,44 @@
# &yet (And Yet)
## Company blurb
[&yet](https://andyet.com) is about people. Were known as a design and development consultancy (specializing in Node, React, and realtime), but we dont fit neatly in a box.
We design and [develop custom software](https://andyet.com/software) for web, mobile, desktop, chat, and voice.
We enable millions of people to make super simple video calls with [Talky](https://talky.io).
We pioneer software and standards for [realtime communications](https://andyet.com/realtime).
We [wrote the book](https://gatherthepeople.com) on taking a human approach to marketing for people who would rather make what they love than persuade people to buy it.
We create high-impact conference experiences such as [RealtimeConf](http://experience.realtimeconf.com) and more recently[&yetConf](http://andyetconf.com).
[Learn more about our team](https://andyet.com/about).
## Company size
20+
## Remote status
We employ several strategies to ensure an inclusive and collaborative environment for all our employees.
To communicate we use [Slack](https://slack.com) (text-chat), our own product [Talky](https://talky.io) (video chat and meetings), [Twist](https://twistapp.com) (daily check-ins) and [GitHub](https://github.com) (organization wide discussions).
One-on-ones and bi-weekly company-wide updates are a crucial part of staying connected and understanding our team as things change. We encourage employees to use these meetings to bring up frustrations, ideas, or whatever they need in order to be their best selves and to do their best work.
At least once a year we organize an in-person all-hands team week. Its the best.
## Region
&yet has one office located in Richland, WA. Currently ten people are working remotely out of Seattle, Portland, Folsom, Phoenix, Denver, Kansas City, Frankfurt, Oslo, and Melbourne. The most significant timezone difference is 17 hours.
## Company technologies
* Node.js
* React
* WebRTC
* Pug
* Stylus
## Office locations
[Fuse Coworking in Richland, WA](https://goo.gl/maps/oJaAQFf12tv)
## How to apply
No current openings.

View file

@ -0,0 +1,12 @@
# AngularClass
## Company blurb
We don't have much information about this company yet! (But the warning emoji
is missing from this paragraph)
If you know something we don't, help us fill it in! Here's how:
- Read our [Contributing Guidelines](https://github.com/remoteintech/remote-jobs/blob/master/CONTRIBUTING.md)
- Have a look at our [example company profile](https://github.com/remoteintech/remote-jobs/blob/master/company-profiles/example.md)
- Follow the structure of the example profile and [send us a pull request with your changes to this file!](https://github.com/remoteintech/remote-jobs/edit/master/company-profiles/angularclass.md)

View file

@ -0,0 +1,17 @@
# Test data
This company table contains some entries with text (parts of the company name)
outside of the link to the company profile, including spurious UTF-8 encoding
of the ⚠ symbol.
## Companies
Name | Website | Region
------------ | ------- | -------
[&yet](/company-profiles/and-yet.md) | https://andyet.com | Worldwide
[10up](/company-profiles/10up.md) agency | https://10up.com/ | Worldwide
[17hats](/company-profiles/17hats.md) | https://www.17hats.com/ | Worldwide
[18F](/company-profiles/18f.md) | https://18f.gsa.gov/ | USA
[45royale](/company-profiles/45royale.md) ⚠ | http://45royale.com/ |
[Aerolab](/company-profiles/aerolab.md) ⚠️️ more text | https://aerolab.co/ |
[AngularClass](/company-profiles/angularclass.md) text ⚠️️ | https://angularclass.com | PST Timezone

View file

@ -0,0 +1,35 @@
# 10up, A WordPress Development Agency
## Company blurb
We make websites and content management simple and fun with premiere web design & development consulting services, by contributing to open platforms like WordPress, and by providing tools and products (like [PushUp](https://pushupnotifications.com/)) that make web publishing a cinch.
At 10up, we dont just “make” things we engineer them. Were a group of people built to solve problems; made to create; wired to delight. From beautiful pixels to beautiful code, we constantly improve the things around us, applying our passions to our clients projects and goals.
Weve had the privilege of working on big web projects for clients as diverse as TechCrunch, ESPNs FiveThirtyEight and Grantland, SurveyMonkey, Junior Diabetes Research Foundation (JDRF), and Google.
## Company size
125 and growing, spread across web engineering, systems, design, project management, strategy/accounts, and operations.
## Remote status
10up didnt integrate remote work we intended to be remote from the start! Being remote allows us to find talent no matter where they're located, scale up to meet needs with relative fluidity, and have been bootstrapped from the start. We also recognize the challenges of working remotely, and put a lot of effort into in-person meetups, communication tools, and ensuring that employees have the benefits and support they need no matter where they are.
## Region
We have employees all around the world, from across the US to the UK to South Africa to the Philippines. Most are currently located in North America, a number travel frequently, and some even work nomadically.
## Company technologies
* WordPress
* PHP
* Sass
* Git
* Vagrant
* Nginx
* Memcache
## How to apply
Check out our [careers page](https://10up.com/careers/) and send an email to jobs@10up.com. Our amazing Recruitment Manager Christine Garrison will be on the other end.

View file

@ -0,0 +1,21 @@
# 17hats
## Company blurb
Are you ready to stop juggling multiple apps to manage your business? From booking clients, to managing projects, to controlling your finances, with 17hats you have everything you need to manage your business and clients anytime, anywhere. Pretty neat if we say so ourselves! We created 17hats specifically for “businesses of one” with a focus on simplicity and ease of use.
## Company size
11-50
## Remote status
Every engineer on our team works remotely. That being said, we do need someone who can work California business hours (9am-6pm PST) and who is available for emergencies at night. If you live in the LA area, you are welcome to be in our cool office in Pasadena. Bonus points if you can curse in Dutch.
## Region
Worldwide
## Company technologies
iOS, React, Knockout, Rails, Perl, HTML, Sql, Ruby, JQuery

View file

@ -0,0 +1,47 @@
# 18F
## Company blurb
18F is a civic consultancy for the government, inside the government, working with agencies to rapidly deploy tools and services that are easy to use, cost efficient, and reusable. Our goal is to change how the government buys and develops digital services by helping agencies adopt modern techniques that deliver superior products.
We are transforming government from the inside out, creating cultural change by working with teams inside agencies who want to create great services for the public.
We are a trusted partner for agencies working to transform how they build and buy tools and services in a user-centered way.
We will accomplish our mission by:
putting the needs of the public first
being design-centric, agile, open, and data-driven
deploying tools and services early and often
## Company size
100+
## Remote status
18F employees live all over the country. We work out of homes in Dayton and Tucson, St. Louis and Chapel Hill — and in federal buildings in San Francisco, Chicago, New York City, and Washington D.C.
That means many of our project teams are also made up of distributed employees working all over the country. For example, you may have a developer in Austin, a designer in Washington D.C., and a content strategist in Portland — but theyre all working on the same team and with the same partners.
Because we work on distributed teams so frequently, we've developed certain strategies for working well as a collaborative operation.
[We have a “remote first” mindset.](https://18f.gsa.gov/2015/10/15/best-practices-for-distributed-teams/)
## Region
U.S. citizens, non-citizens who are nationals of the U.S., or people who have been admitted to the U.S. for permanent residence and hold a valid green card.
## How to apply
[Open positions](https://pages.18f.gov/joining-18f/open-positions/)
If you want to apply directly to 18F please email join18f@gsa.gov. We dont require a formal cover letter, but let us know more about you:
Send your current resume, preferably as a PDF.
Link to your GitHub profile, design portfolio, or attach a writing sample.
Specify what role youd like to be considered for. Check out our openings here.
If you're a Veteran of the U.S. Armed Forces or if you are eligible for "derived" preference, please mention that in your email so we can give you priority consideration.
Don't see an opening that suits you? Tell us what you want to do!
[How to apply](https://pages.18f.gov/joining-18f/how-to-apply/)

View file

@ -0,0 +1,11 @@
# 45royale
## Company blurb
⚠ We don't have much information about this company yet!
If you know something we don't, help us fill it in! Here's how:
- Read our [Contributing Guidelines](https://github.com/remoteintech/remote-jobs/blob/master/CONTRIBUTING.md)
- Have a look at our [example company profile](https://github.com/remoteintech/remote-jobs/blob/master/company-profiles/example.md)
- Follow the structure of the example profile and [send us a pull request with your changes to this file!](https://github.com/remoteintech/remote-jobs/edit/master/company-profiles/45royale.md)

View file

@ -0,0 +1,11 @@
# Aerolab
## Company blurb
⚠ We don't have much information about this company yet!
If you know something we don't, help us fill it in! Here's how:
- Read our [Contributing Guidelines](https://github.com/remoteintech/remote-jobs/blob/master/CONTRIBUTING.md)
- Have a look at our [example company profile](https://github.com/remoteintech/remote-jobs/blob/master/company-profiles/example.md)
- Follow the structure of the example profile and [send us a pull request with your changes to this file!](https://github.com/remoteintech/remote-jobs/edit/master/company-profiles/aerolab.md)

View file

@ -0,0 +1,44 @@
# &yet (And Yet)
## Company blurb
[&yet](https://andyet.com) is about people. Were known as a design and development consultancy (specializing in Node, React, and realtime), but we dont fit neatly in a box.
We design and [develop custom software](https://andyet.com/software) for web, mobile, desktop, chat, and voice.
We enable millions of people to make super simple video calls with [Talky](https://talky.io).
We pioneer software and standards for [realtime communications](https://andyet.com/realtime).
We [wrote the book](https://gatherthepeople.com) on taking a human approach to marketing for people who would rather make what they love than persuade people to buy it.
We create high-impact conference experiences such as [RealtimeConf](http://experience.realtimeconf.com) and more recently[&yetConf](http://andyetconf.com).
[Learn more about our team](https://andyet.com/about).
## Company size
20+
## Remote status
We employ several strategies to ensure an inclusive and collaborative environment for all our employees.
To communicate we use [Slack](https://slack.com) (text-chat), our own product [Talky](https://talky.io) (video chat and meetings), [Twist](https://twistapp.com) (daily check-ins) and [GitHub](https://github.com) (organization wide discussions).
One-on-ones and bi-weekly company-wide updates are a crucial part of staying connected and understanding our team as things change. We encourage employees to use these meetings to bring up frustrations, ideas, or whatever they need in order to be their best selves and to do their best work.
At least once a year we organize an in-person all-hands team week. Its the best.
## Region
&yet has one office located in Richland, WA. Currently ten people are working remotely out of Seattle, Portland, Folsom, Phoenix, Denver, Kansas City, Frankfurt, Oslo, and Melbourne. The most significant timezone difference is 17 hours.
## Company technologies
* Node.js
* React
* WebRTC
* Pug
* Stylus
## Office locations
[Fuse Coworking in Richland, WA](https://goo.gl/maps/oJaAQFf12tv)
## How to apply
No current openings.

View file

@ -0,0 +1,11 @@
# AngularClass
## Company blurb
⚠ We don't have much information about this company yet!
If you know something we don't, help us fill it in! Here's how:
- Read our [Contributing Guidelines](https://github.com/remoteintech/remote-jobs/blob/master/CONTRIBUTING.md)
- Have a look at our [example company profile](https://github.com/remoteintech/remote-jobs/blob/master/company-profiles/example.md)
- Follow the structure of the example profile and [send us a pull request with your changes to this file!](https://github.com/remoteintech/remote-jobs/edit/master/company-profiles/angularclass.md)

View file

@ -33,7 +33,9 @@ exports.runValidationWithFixtures = ( dirName, env = {} ) => {
} }
if ( process.env.DUMP_OUTPUT ) { if ( process.env.DUMP_OUTPUT ) {
output.forEach( s => console.log( "'%s',", s.replace( /'/g, "\\'" ) ) ); output.forEach( s => {
console.log( "'%s',", s.replace( /('|\\)/g, "\\$1" ) );
} );
} }
return { output, exitCode }; return { output, exitCode };

View file

@ -15,7 +15,7 @@ describe( 'validation script errors', () => {
it( 'should catch missing company names', () => { it( 'should catch missing company names', () => {
expect( runValidationWithFixtures( 'missing-company-names' ) ).to.eql( { expect( runValidationWithFixtures( 'missing-company-names' ) ).to.eql( {
exitCode: 10, exitCode: 11,
output: [ output: [
'README.md: Company "⚠⚠⚠" has no linked Markdown profile (".md")', 'README.md: Company "⚠⚠⚠" has no linked Markdown profile (".md")',
'README.md: Missing company name: <td></td><td><a href="https://andyet.com">https://andyet.com</a></td><td>Worldwide</td>', 'README.md: Missing company name: <td></td><td><a href="https://andyet.com">https://andyet.com</a></td><td>Worldwide</td>',
@ -25,6 +25,7 @@ describe( 'validation script errors', () => {
'README.md: Missing company name: <td><a href="/company-profiles/17hats.md"></a></td><td><a href="https://www.17hats.com/">https://www.17hats.com/</a></td><td>Worldwide</td>', 'README.md: Missing company name: <td><a href="/company-profiles/17hats.md"></a></td><td><a href="https://www.17hats.com/">https://www.17hats.com/</a></td><td>Worldwide</td>',
'README.md: Missing company name: <td></td><td><a href="https://18f.gsa.gov/">https://18f.gsa.gov/</a></td><td>USA</td>', 'README.md: Missing company name: <td></td><td><a href="https://18f.gsa.gov/">https://18f.gsa.gov/</a></td><td>USA</td>',
'README.md: Company "" has no linked Markdown profile (".md")', 'README.md: Company "" has no linked Markdown profile (".md")',
'10up.md: Profile looks complete, but the main readme contains a warning emoji.',
'18f.md: No link to company profile from readme', '18f.md: No link to company profile from readme',
'and-yet.md: No link to company profile from readme', 'and-yet.md: No link to company profile from readme',
], ],
@ -94,4 +95,31 @@ describe( 'validation script errors', () => {
], ],
} ); } );
} ); } );
it( 'should catch text outside of links in readme', () => {
expect( runValidationWithFixtures( 'name-outside-link' ) ).to.eql( {
exitCode: 3,
output: [
'README.md: Extra text in company name: "10up", "10up agency"',
'README.md: Extra text in company name: "Aerolab", "Aerolab more text"',
'README.md: Extra text in company name: "AngularClass", "AngularClass text"',
],
} );
} );
it( 'should catch mismatched "incomplete profile" indicators', () => {
expect( runValidationWithFixtures( 'mismatched-incomplete-indicators' ) ).to.eql( {
exitCode: 7,
output: [
'10up.md: Profile is marked as complete, but it only contains a "Company blurb" heading.',
'17hats.md: Profile looks complete, but the "Company blurb" contains a warning emoji.',
'18f.md: Profile looks incomplete, but the main readme does not contain a warning emoji.',
'45royale.md: Profile is marked as incomplete, but it contains multiple sections.',
'45royale.md: Please remove the warning emoji from the "Company blurb" section and the main readme.',
'aerolab.md: Profile looks incomplete, but the "Company blurb" does not contain a warning emoji.',
'and-yet.md: Profile looks complete, but the main readme contains a warning emoji.',
'angularclass.md: Profile looks incomplete, but the "Company blurb" does not contain a warning emoji.',
],
} );
} );
} ); } );

View file

@ -2,7 +2,7 @@ const { expect } = require( 'chai' );
const { runValidationWithFixtures } = require( './lib' ); const { runValidationWithFixtures } = require( './lib' );
describe( 'validation script', () => { describe( 'validation script ok', () => {
it( 'should pass with valid data', () => { it( 'should pass with valid data', () => {
expect( runValidationWithFixtures( 'valid' ) ).to.eql( { expect( runValidationWithFixtures( 'valid' ) ).to.eql( {
exitCode: 0, exitCode: 0,