Мы в SerpApi показываем результаты интеграционных тестов на страницах документации. Любой может просмотреть исходный код наших файлов RSpec и его вывод. На всех страницах документации есть значок сборки, который указывает на страницу со списком тестовых файлов. Каждый тестовый файл также отображает значок сборки.

Вот как это работает на наших страницах документации.

Мы начали с Shields.io, но потом реализовали значки вручную в React без SVG и зависимостей. Полный код на Codesandbox.io.

Компонент значка

import React from "react";
import "./BuildBadge.css";
function BuildBadge({ status, summary, page_url }) {
  let spinner;
  if (status === "loading") {
    spinner = <span className="loader" />;
  }
return (
    <div className="build-badge-container">
      <a href={page_url}>
        <div className="badge-name">build</div>
        <div className={`badge-status ${status}`}>
          {spinner}
          <span>{summary}</span>
        </div>
      </a>
    </div>
  );
}

Стили значков

/* Thanks to https://shields.io/category/build for inspiration */
.build-badge-container {
  height: 20px;
  min-width: 200px;
margin: 2px 0;
}
.badge-name,
.badge-status {
  display: inline-block;
  height: 18px;
  color: #fff;
  font-family: Verdana, DejaVu Sans, sans-serif;
  font-size: 11px;
  line-height: 1.538;
  letter-spacing: 0px;
text-shadow: 0px 1px rgba(1, 1, 1, 0.3);
}
.badge-name {
  background: rgb(95, 95, 95);
  background: linear-gradient(
    180deg,
    rgba(95, 95, 95, 1) 0%,
    rgba(78, 78, 78, 1) 100%
  );
  border-radius: 3px 0px 0px 3px;
  padding: 2px 4px 0px 6px;
}
.badge-status {
  border-radius: 0px 3px 3px 0px;
  padding: 2px 6px 0px 4px;
}
.badge-status.loading {
  background: rgb(223, 179, 23);
  background: linear-gradient(
    180deg,
    rgba(223, 179, 23, 1) 0%,
    rgba(206, 162, 6, 1) 100%
  );
}
.badge-status.success {
  background: rgb(223, 179, 23);
  background: linear-gradient(
    180deg,
    rgba(85, 202, 48, 1) 0%,
    rgba(62, 183, 17, 1) 100%
  );
}
.badge-status.pending {
  background: rgb(247, 132, 71);
  background: linear-gradient(
    180deg,
    rgba(247, 132, 71, 1) 0%,
    rgba(228, 113, 49, 1) 100%
  );
}
.badge-status.error,
.badge-status.failed {
  background: rgb(221, 103, 81);
  background: linear-gradient(
    180deg,
    rgba(221, 103, 81, 1) 0%,
    rgba(201, 84, 61, 1) 100%
  );
}
/* Thanks to
  https://vineethtrv.github.io/loader/
  and
  https://www.w3schools.com/howto/howto_css_loader.asp
  for inspiration
*/
.loader {
  position: relative;
  padding-left: 20px; /* spinner size * 2 */
}
.loader::before {
  content: "";
  display: inline-block;
  position: absolute;
left: 0;
  top: 0;
  margin: 0px 6px 0px 2px;
border: 1px solid transparent;
  border-radius: 50%;
  border-top: 2px solid #fff;
  width: 10px;
  height: 10px;
animation: spin 1s ease-out infinite;
}
@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

использование

function App() {
  return (
    <div className="App">
      <BuildBadge
        status="loading"
        summary="loading..."
        page_url="https://serpapi.com/search-api"
      />
      <BuildBadge
        status="error"
        summary="125 examples, 3 failures"
        page_url="https://serpapi.com/organic-results"
      />
      <BuildBadge
        status="success"
        summary="50 examples, 0 failures"
        page_url="https://serpapi.com/google-scholar-api"
      />
      <BuildBadge
        status="pending"
        summary="87 examples, 2 pending"
        page_url="https://serpapi.com/images-results"
      />
    </div>
  );
}

Спасибо Shields.io, Vineeth TR и W3Schools за вдохновение.