diff --git a/.babelrc b/.babelrc
new file mode 100644
index 00000000..34bc6d2c
--- /dev/null
+++ b/.babelrc
@@ -0,0 +1,3 @@
+{
+ "plugins": ["@babel/plugin-transform-modules-commonjs"]
+}
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 00000000..1923d410
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,8 @@
+root = true
+
+[*]
+indent_style = space
+indent_size = 2
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
diff --git a/.eslintignore b/.eslintignore
new file mode 100644
index 00000000..43df0212
--- /dev/null
+++ b/.eslintignore
@@ -0,0 +1 @@
+src/data
diff --git a/.eslintrc b/.eslintrc
new file mode 100644
index 00000000..3d062a23
--- /dev/null
+++ b/.eslintrc
@@ -0,0 +1,18 @@
+{
+ "env": {
+ "browser": true
+ },
+ "parserOptions": {
+ "ecmaVersion": 2018,
+ "sourceType": "module"
+ },
+ "extends": "eslint:recommended",
+ "rules": {
+ "no-console": "warn",
+ "import/extensions": 0,
+ "no-var": "error",
+ "prefer-const": "error",
+ "eqeqeq": "error",
+ "indent": ["error", 2]
+ }
+}
diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml
new file mode 100644
index 00000000..5156520e
--- /dev/null
+++ b/.github/workflows/playwright.yml
@@ -0,0 +1,27 @@
+name: Playwright Tests
+on:
+ push:
+ branches: [ main ]
+ pull_request:
+ branches: [ main ]
+jobs:
+ test:
+ timeout-minutes: 60
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - uses: actions/setup-node@v3
+ with:
+ node-version: 18
+ - name: Install dependencies
+ run: npm ci
+ - name: Install Playwright Browsers
+ run: npx playwright install --with-deps
+ - name: Run Playwright tests
+ run: npx playwright test
+ - uses: actions/upload-artifact@v3
+ if: always()
+ with:
+ name: playwright-report
+ path: playwright-report/
+ retention-days: 30
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..3d7966e7
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+.DS_Store
+*.swp
+coverage/
+node_modules/
+/test-results/
+/playwright-report/
+/playwright/.cache/
+.vscode/
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..fe985489
--- /dev/null
+++ b/README.md
@@ -0,0 +1,1057 @@
+# Dataverse
+
+## Índice
+
+* [1. Preámbulo](#1-preámbulo)
+* [2. Resumen del proyecto](#2-resumen-del-proyecto)
+* [3. Consideraciones generales](#3-consideraciones-generales)
+* [4. Funcionalidades](#4-funcionalidades)
+* [5. Consideraciones técnicas](#5-consideraciones-técnicas)
+* [6. Hitos](#6-hitos)
+* [7. Criterios de aceptación mínimos del proyecto](#7-criterios-de-aceptación-mínimos-del-proyecto)
+* [8. Hacker edition](#8-hacker-edition)
+* [9. Objetivos de aprendizaje](#9-objetivos-de-aprendizaje)
+* [10. Pistas, tips y lecturas complementarias](#10-pistas-tips-y-lecturas-complementarias)
+* [11. Consideraciones para pedir tu Project Feedback](#11-consideraciones-para-pedir-tu-project-feedback)
+
+***
+
+## 1. Preámbulo
+
+Según [Forbes](https://www.forbes.com/sites/bernardmarr/2018/05/21/how-much-data-do-we-create-every-day-the-mind-blowing-stats-everyone-should-read),
+el 90% de la data que existe hoy ha sido creada durante los últimos dos años.
+Cada día generamos 2.5 millones de terabytes de datos, una cifra sin
+precedentes.
+
+No obstante, los datos por sí mismos son de poca utilidad. Para que esas
+grandes cantidades de datos se conviertan en **información** fácil de leer para
+las usuarias, necesitamos entender y procesar estos datos. Una manera simple de
+hacerlo es creando _interfaces_ y _visualizaciones_.
+
+En la siguiente imagen, podrás ver cómo con la data que que se ve en la parte
+izquierda se puede construir una interfaz amigable y entendible por las
+usuarias, al lado derecho.
+
+![pokemon-data-to-ui](https://user-images.githubusercontent.com/12631491/218505816-c6d11758-9de4-428f-affb-2a56ea4d68c4.png)
+
+## 2. Resumen del proyecto
+
+En este proyecto **construirás una _página web_ para visualizar un
+_conjunto (set) de datos_** que vas a generar con [prompting](https://www.itmadrid.com/que-es-un-prompt-en-inteligencia-artificial-ia/).
+Esta página web se adecuará a lo que descubras que tu usuaria
+necesita.
+
+Además, en este proyecto utilizarás herramientas de
+[inteligencia artificial](https://es.wikipedia.org/wiki/Inteligencia_artificial)
+como [ChatGPT](https://openai.com/chatgpt), [ExplainDev](https://explain.dev/),
+entre otras para generar un set de datos en un archivo javascript.
+
+El propósito de generar los datos en esta manera es brindarte la oportunidad de
+adentrarte en el empleo de herramientas impulsadas por la inteligencia
+artificial, así como en [técnicas de prompting](https://learnprompting.org/es/docs/intro).
+
+Como entregable final tendrás una página web que permita **visualizar la data,
+filtrarla, ordenarla y calcular alguna estadística**. Con estadística
+nos referimos a distintos cálculos que puedes hacer con los datos para mostrar
+información aún más relevante a las usuarias (promedio, el valor máximo
+o mínimo, etc).
+
+## 3. Consideraciones generales
+
+* Este proyecto se debe resolver en duplas.
+* El rango de tiempo estimado para completar el proyecto es de 4 a 5 Sprints.
+* El tiempo estimado que deberías dedicar a la [generación de los datos](#generar-los-datos)
+ es de máximo un sprint. Además, al final del proyecto deberás presentar
+ un [screenshot del prompt utilizado](#prompt-utilizado).
+* Si ves que te va a tomar más tiempo,
+ deberás utilizar los datos de ejemplo que los vas a encontrar en
+ esta ruta: `./src/data/dataset.js`.
+* El proyecto será entregado subiendo tu código a GitHub (commit/push) y la
+ interfaz será desplegada usando [GitHub Pages](https://pages.github.com/).
+
+## 4. Funcionalidades
+
+Como entregable final tendrás una página web que permita **visualizar la data,
+filtrarla, ordenarla y calcular alguna estadística**.
+
+Aquí definimos en más detalle las funcionalidades mínimas que debe
+tener:
+
+* La aplicación debe permitir a la usuaria ver los items de la data en una visualización,
+ que puede ser [tipo tarjetas](http://www.uxables.com/diseno-ux-ui/que-es-y-como-disenar-una-card/)
+ o cualquier otra forma que tú decidas como la adecuada (pero desde aquí
+ referimos a los items como "tarjetas"). **Cada una de las tarjetas debe estar
+ contenida en un elemento `
` y estos a su vez contenido en
+ un elemento ``.**
+
+* El elemento `` deberá ser hijo de un elemento con atributo _id_
+ de valor "root". **Este es un paso importante para que tu**
+ **aplicación tenga la estructura requerida**
+
+* Las tarjetas deben resaltar los valores de las propiedades de la data que
+ le interesaría a la usuaria ver. Por ejemplo: nombre, fecha, imagen, etc.
+ **Si vas a filtrar u ordenar por una propiedad, la tarjeta tiene que mostrar
+ el valor de esta propiedad a la usuaria.**
+
+* La interfaz debe estructurar semánticamente la data usando el estándar
+ [microdatos](https://developer.mozilla.org/es/docs/Web/HTML/Microdata).
+ Es obligatorio usar al menos los atributos
+ [`itemscope`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/itemscope),
+ [`itemtype`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/itemtype)
+ y el atributo
+ [`itemprop`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/itemprop).
+
+ Por ejemplo, la siguiente data correspondiente a Ada Lovelace:
+
+ ```json
+ {
+ "id": "ada-lovelace",
+ "name": "Ada Lovelace",
+ "shortDescription": "Pionera de la informática, fue la primera programadora.",
+ "description": "Una visionaria del siglo XIX ...",
+ "imageUrl": "URL_DE_LA_IMAGEN_GENERADA",
+ "facts": {
+ "yearOfBirth": 1843,
+ "placeOfBirth": "London, England",
+ "mainField": "Computer Science",
+ }
+ }
+ ```
+
+ puede ser estructurada semánticamente en HTML como:
+
+ ```html
+
+
+ Nombre: Ada Lovelace
+ Descripción: Pionera de la informática, fue la primera programadora.
+ Año de nacimiento: 1843
+ Lugar de nacimiento: London, England
+ Campo de desempeño: Computer Science
+
+ ```
+
+* La aplicación debe calcular y visualizar una estadística de la data. Puede
+ ser una propiedad computada de cada item, como una propiedad adicional
+ (por ejemplo, el índice de masa corporal de cada pokemon) o unas estadísticas
+ de la data completa (por ejemplo, total de personas nacidas en los años 80s).
+
+* La aplicación debe permitir a la usuaria filtrar la data. Deberás usar
+ un elemento [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select)
+ con [un atributo de datos](https://developer.mozilla.org/es/docs/Learn/HTML/Howto/Use_data_attributes)
+ `data-testid="select-filter"`, y un atributo `name` con el nombre
+ de la propiedad por la que filtrará (por ejemplo, si vas a filtrar por "type",
+ el `` tendrá `name="type"`). Los `` de este `` deberán
+ tener en el atributo `value` el valor del filtro (por ejemplo, si vas a filtrar
+ por type "fire" sería `Fire `).
+
+* La aplicación debe permitir a la usuaria ordenar la data.
+ - Tendrá al menos un control `` para ordenar.
+ - Si usas solo un control ``, debe tener
+ [un atributo de datos](https://developer.mozilla.org/es/docs/Learn/HTML/Howto/Use_data_attributes)
+ `data-testid="select-sort"` y un atributo `name` con el nombre de la
+ propiedad por la que ordenará. (por ejemplo, si vas a ordenar por
+ "num" seria `name="num"`). Este `` tendrá dos [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option)
+ con `value` `asc` y `desc`, para ordenar ascendente y descendente la data
+ respectivamente (por ejemplo, ` A - Z `).
+ - Una alternativa es ofrecer la usuaria un ordenamiento mas complejo.
+ Podrías implementar ordenar por varios propiedades. En este caso sería con
+ un `` con un atributo de datos `data-testid="select-sort"`, y que
+ contiene hijos `` con un `value` del nombre de la propiedad con
+ cual vas a ordenar. (Por ejemplo, ` Nombre `).
+ También, necesitarás otro control (``,``, etc.) para decir
+ que el ordenamiento es ascendente o descendente. Este control secundaria
+ tendrá un atributo `name="sort-order"`, y tiene values `asc` y `desc`.
+
+* Las funcionalidades de ordenar deben operar sobre la data filtrada.
+ Por ejemplo, si filtro los pokemones de tipo fuego y luego los ordeno por
+ nombre ascendente, la aplicación deberá mantener el filtro aplicado y
+ ordenar los pokemones de tipo fuego.
+
+* La aplicación debe permitir a la usuaria reiniciar la aplicación, limpiando
+ filtros y ordenamiento, con un `` con un atributo de datos
+ `data-testid="button-clear"`.
+
+* Las operaciones de filtrar, ordenar, limpiar, etc. no deben recargar
+ la página, si no que deben agregar el contenido en una manera
+ dinámica via javascript.
+
+* La aplicación será _responsive_, es decir, debe visualizarse sin problemas
+ desde distintos tamaños de pantallas: móviles, tablets y desktops.
+
+Los siguientes wireframes, son ejemplos de una interfaz que puede cumplir con esta
+funcionalidad. Como podrás ver, estos diseños cumplen con la metodología
+[Mobile First](https://developer.mozilla.org/es/docs/Glossary/Mobile_First), la misma
+que te recomendamos utilizar en todos tus proyectos:
+
+Diseño Mobile:
+
+* [Wireframe mobile 1](https://github.com/Laboratoria/curriculum/assets/123121338/54711bb7-cb05-448e-b677-3cbd9bf13c14)
+* [Wireframe mobile 2](https://github.com/Laboratoria/curriculum/assets/123121338/bf96d3ce-150f-47a2-a605-2efac2e0497b)
+
+Diseño Desktop:
+
+* [Wireframe desktop 1](https://github-production-user-asset-6210df.s3.amazonaws.com/92090/261137084-1625aeb8-883c-4b79-86da-5fab34fa5b88.png)
+* [Wireframe desktop 2](https://github-production-user-asset-6210df.s3.amazonaws.com/92090/261137087-6cef16bc-643a-4d6d-bc1c-e0daaeb21c88.png)
+
+## 5. Consideraciones técnicas
+
+La lógica del proyecto debe estar implementada completamente en JavaScript
+(ES6), HTML y CSS. En este proyecto NO está permitido usar librerías o
+frameworks, solo [vanilla JavaScript](https://medium.com/laboratoria-how-to/vanillajs-vs-jquery-31e623bbd46e),
+con la excepción de librerías para hacer gráficas (charts); ver
+[_Parte opcional_](#8-hacker-edition) más arriba.
+
+El _boilerplate_ contiene una estructura de archivos como punto de partida así
+como toda la configuración de dependencias:
+
+```text
+.
+├── README.md
+├── package.json
+├── src
+| ├── data
+| | └── dataset.js (La que hayas generado con la IA)
+| ├── dataFunctions.js
+| ├── view.js
+| ├── index.html
+| ├── main.js
+| └── style.css
+└── test
+ └── data.js
+ └── dataFunctions.spec.js
+ └── tests-read-only
+
+```
+
+### `src/index.html`
+
+Como en el proyecto anterior, existe un archivo `index.html`. Como ya sabes,
+acá va la página que se mostrará a la usuaria. También nos sirve para indicar
+qué scripts se usarán y unir todo lo que hemos hecho.
+
+### `src/main.js`
+
+Recomendamos usar `src/main.js` para todo tu código que tenga que ver con
+mostrar los datos en la pantalla. Con esto nos referimos básicamente a la
+interacción con el DOM. Operaciones como creación de nodos, registro de
+manejadores de eventos (_event listeners_ o _event handlers_).
+
+En este archivo encontrarás una serie de _imports_ listos para _cargar_
+las diferentes fuentes de datos.
+
+Por ejemplo, los datos con los que vas a trabajar,
+los encontrarás en la siguiente línea:
+
+```js
+import data from './data/dataset.js';
+```
+
+### `src/dataFunctions.js`
+
+El corazón de este proyecto es la manipulación de datos a través de arreglos
+y objetos.
+
+Este archivo va a contener toda la funcionalidad que corresponda
+a obtener, procesar y manipular datos (tus funciones). Por ejemplo:
+
+* `filterData(data, filterBy, value)`: esta función recibe tres parámetros.
+ El primer parámetro, `data`, nos entrega los datos.
+ El segundo parámetro, `filterBy`, nos dice con respecto a cuál de los campos de
+ la data se quiere filtrar.
+ El tercer parámetro, `value`, indica el valor de campo que queremos filtrar.
+
+* `sortData(data, sortBy, sortOrder)`: esta función `sort` u ordenar
+ recibe tres parámetros.
+ El primer parámetro, `data`, nos entrega los datos.
+ El segundo parámetro, `sortBy`, nos dice con respecto a cuál de los campos de
+ la data se quiere ordenar.
+ El tercer parámetro, `sortOrder`, indica si se quiere ordenar de manera
+ ascendente o descendente.
+
+* `computeStats(data)`: la función `compute` o calcular, nos permitirá hacer
+ cálculos estadísticos básicos para ser mostrados de acuerdo a la data
+ proporcionada, esta función debe usar el método reduce.
+
+Estas funciones deben ser [_puras_](https://medium.com/laboratoria-developers/introducci%C3%B3n-a-la-programaci%C3%B3n-funcional-en-javascript-parte-2-funciones-puras-b99e08c2895d)
+e independientes del DOM. Estas funciones serán después usadas desde el archivo
+`src/main.js`, al cargar la página, y cada vez que la usuaria interactúe
+(click, filtrado, ordenado, ...).
+
+### `src/data`
+
+En esta carpeta están los datos con los que vas a trabajar (los datos de ejemplo
+o los datos que generarías con ayuda de la inteligencia artificial).
+
+### `test/dataFunctions.spec.js`
+
+En este archivo tendrás hacer pruebas unitarias de las funciones
+implementadas en el archivo `dataFunctions.js`. (`filterBy`, `sortBy`, etc.)
+
+### `test/data.js`
+
+En esta archivo puedes construir y exportar data "mock" para usar en los tests.
+Es mas fácil probar un arreglo de 5 elementos de un arreglo de 24, por eso
+vas a crear una muestra de la data que quieres probar. Como mínimo
+debes exportar un variable se llama `data`, pero puedes definir y exportar mas
+si sea necesario para tus tests.
+
+### `src/view.js`
+
+Para alcanzar una mejor separación de responsabilidades en el código, éste
+archivo debe tener todas las funciones que se utilizarán para renderizar
+los elementos dinámicamente.
+
+Al menos se requiere una función obligatoria:
+
+* `renderItems(data)`: esta función recibe el arreglo de data para renderizar
+ los elementos de cada item, y debería volver un elemento DOM o
+ un string de HTML.
+
+Recuerda que todas las funciones que se encuentren en este
+archivo deberán ser exportadas para poder ser utilizadas en
+otros archivos.
+
+Recomendamos esta estructura para no solo proporcionar un marco claro y
+organizado para el proyecto, facilitando la navegación comprensión, y
+escalabilidad del código, sino también para seguir un principio de diseño
+[Separación de Responsabilidades](https://dev.to/tamerlang/separation-of-concerns-the-simple-way-4jp2)
+en codigo, donde cada archivo y carpeta
+tiene una responsabilidad específica. La responsabilidad de los funciones en
+`view.js` es para crear partes del DOM con la data.
+
+Esta no es la única forma de dividir tu código, puedes usar más archivos y
+carpetas, siempre y cuando la estructura sea clara para tus compañeras.
+
+## 6. Hitos
+
+Para abordar eficazmente un problema, resulta crucial adquirir una comprensión
+profunda del mismo. Una estrategia efectiva consiste en desglosarlo en problemas
+más pequeños, lo cual facilitará la identificación de las causas subyacentes y la
+formulación de soluciones más eficientes.
+
+En el contexto de este proyecto, recomendamos adoptar un enfoque por hitos.
+Esta metodología te posibilitará concentrarte en un problema a la vez y
+monitorizar tu avance. A continuación, te proporcionamos un calendario de hitos
+que te servirá para estructurar tu trabajo.
+
+* [Hito 1](./docs/01-milestone.md)
+* [Hito 2](./docs/02-milestone.md)
+* [Hito 3](./docs/03-milestone.md)
+* [Hito 4](./docs/04-milestone.md)
+
+## 7. Criterios de aceptación mínimos del proyecto
+
+### Criterios de código
+
+Con cada objetivo de aprendizaje, evaluamos que el código cumpla con algunos
+criterios. Lo cual no excluye que puedas usar otras opciones, por ejemplo
+en el caso de los selectores, proponemos el uso de `querySelector`,
+no significa que no puedes usar `querySelectorAll` o `getElementById` también.
+
+Puedes ejecutar las pruebas de cada grupo de objetivos de aprendizaje de manera
+individual con los siguientes comandos:
+
+``` sh
+npm run test:oas-html
+npm run test:oas-css
+npm run test:oas-web-api
+npm run test:oas-js
+npm run test:oas-prompting
+npm run test:oas // Esto es para correr todos los tests de OAs
+```
+
+Ejecuta las pruebas mientras desarrollas para confirmar que
+tu proyecto está logrando los objetivos. Si algunas pruebas no pasan,
+no permitas que esto te impida avanzar o finalizar el proyecto.
+Utiliza esta información para ver qué necesitas investigar y
+cambiar en tu código, y consulta con tu coach cualquier
+objetivo que tengas pendiente.
+
+Nota: para el correcto funcionamiento de los tests, es necesario que tengas
+instalado `Node.js (LTS)` con la versión 14.0.0 o superior. Para verificar
+la versión de node ejecuta `node -v` en la terminal. Si el comando no te
+devuelve ninguna versión, necesitas instalarlo, para esto, puedes descargarlo
+desde su [sitio oficial](https://nodejs.org/).
+
+#### HTML
+
+* **Uso de HTML semántico**
+
+ - [ ] Tiene un `` con ``
+ - [ ] Tiene un ``
+ - [ ] Tiene un `` con ``
+ - [ ] Todas las etiquetas de controles (inputs, selects, radio, etc) tienen ``
+ - [ ] Todas las etiquetas `` usan el atributo `for`
+ - [ ] `` esta usado para dibujar la data
+ - [ ] Los hijos de `` usan attributos de [microdata](https://recursivos.com/html/microdatos/)
+ `itemscope` e `itemprop`
+
+Nota: Ten en cuenta que para poder testear el HTML de ``
+y `` en tu proyecto es necesario que ya tengas una data
+creada, ya que a partir de la data se
+crearán estos elementos.
+
+#### CSS
+
+* **Uso de selectores de CSS**
+
+ - [ ] Uso de selector class para los items ` `
+ - [ ] Uso de flexbox en sentido `row` y `column`
+ - [ ] Uso de flexbox para el elemento que contiene los items ` `
+ - [ ] Uso de flexbox para el elemento que contiene los UI inputs
+
+* **Modelo de caja (box model): borde, margen, padding**
+
+ - [ ] Uso de atributos de modelo de caja para los items ` `
+ - [ ] Uso de atributos de modelo de caja para `` o ``
+
+#### Web APIs
+
+* **Uso de selectores del DOM**
+
+ - [ ] Uso de [`querySelector`](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector)
+ para seleccionar elementos del DOM.
+
+* **Manejo de eventos del DOM (listeners, propagación, delegación)**
+
+ - [ ] Uso de `addEventListener` con callback que tiene parámetro de `event`,
+ lo que permite el uso del objeto [`event`](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#event_objects)
+ con `event.target` o `event.currentTarget`.
+ - [ ] La aplicación registra [Event Listeners](https://developer.mozilla.org/en/docs/Web/API/EventTarget/addEventListener)
+ para escuchar `click`, `change`, `keyup` dependiendo del evento que
+ se quiere escuchar.
+
+* **Manipulación dinámica del DOM**
+
+ - [ ] La aplicación actualiza el atributo [`innerHTML`](https://developer.mozilla.org/es/docs/Web/API/Element/innerHTML).
+ - [ ] Uso de `createElement` y `appendChild`, o template strings
+ para crear elementos.
+
+#### JavaScript
+
+* **Variables (declaración, asignación, ámbito)**
+
+ - [ ] La aplicación declara variables con [`let`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let)
+ y [`const`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const)
+ en manera adecuada
+
+* **Uso de condicionales (if-else, switch, operador ternario, lógica booleana)**
+
+ - [ ] La aplicación usa el statement
+ [`if..else`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else)
+ para evaluar condiciones
+
+* **Uso de bucles/ciclos (while, for, for..of)**
+
+ - [ ] La aplicación usa el statement [`for`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for)
+ o método [`forEach`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach)
+ para iterar
+
+* **Funciones (params, args, return)**
+
+ En el archivo `dataFunctions.js` define las siguientes funciones:
+ - [ ] una función `sortBy` que tiene 3 parámetros (`data`, `sortBy`, `sortOrder`)
+ y devuelve el arreglo ordenado
+ - [ ] una función `filterBy` que tiene 3 parámetros (`data`, `filterBy`, `value`)
+ y devuelve el arreglo filtrado
+ - [ ] una función `computeStats` que tiene al menos un parámetro (`data`)
+ y devuelve un valor computado
+
+ Más sobre estos puntos en [la sección dataFunctions.js](#src/dataFunctions.js)
+
+* **Arrays (arreglos)**
+
+ - [ ] Uso de [Arreglos](https://curriculum.laboratoria.la/es/topics/javascript/04-arrays)
+ - [ ] Uso de [Array.prototype.sort() - MDN](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)
+ o [Array.prototype.toSorted - MDN](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Array/toSorted)
+ - [ ] Uso de [Array.prototype.forEach() - MDN](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach)
+ - [ ] Uso de [Array.prototype.map() - MDN](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Array/map)
+ - [ ] Uso de [Array.prototype.filter() - MDN](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Array/filter)
+ - [ ] Uso de [Array.prototype.reduce() - MDN](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce)
+
+* **Objetos**
+
+ - [ ] Uso de notación de punto para [acceder propiedades](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_accessors)
+ - [ ] Uso de notación de brackets para [acceder propiedades](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_accessors)
+
+* **Módulos de ECMAScript (ES Modules)**
+
+ - [ ] La aplicación usa [`import`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import)
+ y [`export`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export)
+ para importar y exportar valores desde un modulo JavaScript.
+
+### Criterios del proyecto
+
+#### Definición del producto
+
+Documenta brevemente tu trabajo en el archivo `README.md` de tu repositorio,
+contándonos cómo fue tu proceso de diseño y cómo crees que el producto resuelve
+el problema (o problemas) que tiene tu usuaria.
+
+#### Historias de usuario
+
+Una vez que entiendas las necesidades de tus usuarias, escribe las
+[Historias de Usuaria](https://es.wikipedia.org/wiki/Historias_de_usuario)
+que representen
+todo lo que la usuaria necesita hacer/ver. Las **Historias de Usuario** deben
+ser el resultado de tu proceso de investigación o _research_ de tus usuarias.
+
+Asegúrate de incluir la definición de terminado (_definition of done_) y los
+Criterios de Aceptación para cada una.
+
+Usa tus historias de usuario para planificar tus sprints dividiendo
+cada historia en tareas.
+
+En la medida de lo posible, termina una Historia de Usuario antes de pasar
+a la siguiente (cumpliendo con la Definición de Terminado y los Criterios de Aceptación).
+
+#### Generar los datos
+
+La temática será a tu gusto, por ejemplo, pueden ser personajes importantes
+en la historia, personajes inventados, países, películas... etc.
+
+En el próximo proyecto, con la ayuda de la inteligencia artificial, deberás
+hacer que la usuaria pueda chatear con la data generada.
+Por ejemplo, si la data está mostrando un país, la usuaria podría
+preguntarle en que año fue fundado o cuál es su capital, etc.
+Tenlo en cuenta a la hora de generar tu dataset.
+
+Esta data la vas a guardar en un archivo javascript. Este archivo,
+debe exportar un arreglo con 24 objetos. Y la estructura de cada objeto
+debe ser la siguiente:
+
+* `id`: Identificador único (no pueden haber dos elementos con el mismo `id`).
+ Debe ser un string de no más de 32 caracteres, en minúscula, compuesto solo
+ por letras, números, underscore (`_`) o guión (`-`). Por ejemplo: `"ada-lovelace"`.
+* `name`: El nombre del personaje, país, película, etc.
+* `shortDescription`: Descripción corta del elemento. Esta descripción deberá
+ tener como máximo 20 palabras.
+* `description`: Descripción extendida del elemento. Esta descripción deberá
+ tener entre 80 y 100 palabras. Al momento de mostrar este dato en pantalla
+ puedes truncarlo para que no ocupe tanto espacio.
+* `imageUrl`: URL de la imagen. Esta imagen será generada a través de alguna
+ herramienta basada en inteligencia artificial. Una vez generada la imagen,
+ y guardada en tu repo, deberás agregar la URL en este campo.
+* `facts`: Un objeto con al menos **3** "hechos" o "info" sobre este elemento, en
+ formato `"nombre": "valor"`, por ejemplo:
+
+ ```json
+ "facts": {
+ "yearOfBirth": 1843,
+ "placeOfBirth": "London, England",
+ "mainField": "Computer Science",
+ }
+ ```
+
+ Los _nombres de las propiedades_, deben estar en formato _camelCase_.
+ Por ejemplo **ninguno** de los siguientes nombres sería válido:
+
+ ```json
+ "facts": {
+ "year_of_birth": 1843,
+ "Place of Birth": "London, England",
+ "MainField": "Computer Science",
+ }
+ ```
+
+ Los _valores de las propiedades_, sólo pueden ser de tipo `number`, `boolean`
+ o un `string` de no más de 64 caracteres (este **no** tiene restricciones sobre
+ el tipo de caracteres que puede contener).
+
+ Y por último ten en cuenta 2 cosas:
+ - Todos los elementos del dataset deben compartir las mismas propiedades en
+ `facts`, es decir, que si un elemento tiene una propiedad `yearOfBirth`,
+ el resto de elementos del array también deben tener esa propiedad.
+ - No es necesario que los nombres de las propiedades estén en inglés,
+ `"lugarDeNacimiento"` es un nombre igual de válido.
+
+* `extraInfo`: Y por último un campo libre opcional, similar a `facts`. Si lo
+ necesitas, aquí puedes poner cualquier otro tipo de información en formato
+ donde puedes poner otra info que necesites en formato `"nombre": "valor"`,
+ pero sin restricciones sobre el tipo de dato del valor. Por ejemplo:
+
+ ```json
+ "extraInfo": {
+ "imagePrompt": "Un texto bien, bien largo...",
+ "writings": [
+ "Vol. 1",
+ "Vol. 2",
+ "Vol. 3",
+ "Vol. 4"
+ ]
+ }
+ ```
+
+Un ejemplo de data, según los requisitos anteriores podría ser:
+
+```js
+export default [
+ {
+ "id": "ada-lovelace",
+ "name": "Ada Lovelace",
+ "shortDescription": "Pionera de la informática, fue la primera programadora.",
+ "description": "Una visionaria del siglo XIX ...",
+ "imageUrl": "URL_DE_LA_IMAGEN_GENERADA",
+ "facts": {
+ "yearOfBirth": 1843,
+ "placeOfBirth": "London, England",
+ "mainField": "Computer Science",
+ }
+ },
+ //... 23 objetos más
+]
+```
+
+La data generada deberás reemplazarla por el contenido de este archivo:
+`./src/data/dataset.js`.
+
+**El tiempo estimado que deberías dedicar a la generación de estos datos
+es de máximo un sprint.** Si transcurrido un sprint, no tienes un
+conjunto de datos generados, deberás
+utilizar los datos de ejemplo ubicados en la ruta:
+`./src/data/dataset.js`.
+
+Las URLs de las imágenes dentro del archivo javascript, deben enlazar
+a las imágenes para cada elemento del array.
+Estas imágenes pueden ser generadas por la inteligencia artificial o imágenes
+que puedas encontrar en la web.
+Para la generación de imágenes te recomendamos usar el
+[generador de imágenes de Bing](https://www.bing.com/create).
+Una vez que tengas la imagen, descárgala u obtén su URL, para agregársela
+al dataset.
+
+Una vez que tengas el archivo javascript completo, recuerda correr los test con
+`npm run test` para verificar que el archivo esté cumpliendo con lo
+solicitado.
+
+Una vez que hayas delimitado tu campo de interés y generado el archivo
+javascript con la asistencia de la inteligencia artificial, dedica
+tiempo a comprender a fondo a tu usuaria y sus
+necesidades específicas. A partir de esta comprensión, podrás diseñar la
+interfaz que facilite una interacción más efectiva y una comprensión más
+completa de los datos presentados.
+
+Nota: no te preocupes si no estás empleando toda la información generada
+en el dataset en tu interfaz, pero es necesario generarla por completo.
+Utilizarás el mismo dataset en el proyecto Dataverse Chat.
+
+#### Prompt utilizado
+
+Dentro del readme que armarás, debe incluir una captura de pantalla
+de tu prompt utilizado para generar los datos. Si utilizaste varios prompts,
+puedes adjuntar todas las capturas que necesites.
+
+#### Diseño de la Interfaz de Usuaria
+
+##### Prototipo de alta fidelidad
+
+Usando los wireframes o bocetos (_sketches_) de tu solución de interfaz
+como base, lo siguiente es diseñar tu Interfaz de Usuaria (UI por sus
+siglas en inglés - _User Interface_). Para eso debes aprender a
+utilizar alguna herramienta de diseño visual.
+Nosotros te recomendamos [Figma](https://www.figma.com/) que es
+una herramienta que funciona en el navegador y, además, puedes crear una cuenta
+gratis. Sin embargo, eres libre de utilizar otros editores gráficos como
+Illustrator, Photoshop, etc.
+
+El diseño debe representar el _ideal_ de tu solución. Digamos que es lo que
+desearías implementar si tuvieras tiempo ilimitado para trabajar. Además, tu
+diseño debe seguir los fundamentos de _visual design_.
+
+Recuerda pedir feedback de tu prototipo a tus compañeras y/o coaches.
+
+#### Testeos de usabilidad
+
+Durante el reto deberás hacer _tests_ de usabilidad con distintos usuarias,
+y con base en los resultados, deberás iterar tus diseños. Cuéntanos
+qué problemas de usabilidad detectaste a través de los _tests_ y cómo los
+mejoraste en tu propuesta final.
+
+#### Implementación de la Interfaz de Usuaria (HTML/CSS/JS)
+
+Luego de diseñar tu interfaz de usuaria deberás trabajar en su implementación.
+**No** es necesario que construyas la interfaz exactamente como la diseñaste.
+Tu tiempo de hacking es escaso, así que deberás priorizar.
+
+Revisa [las funcionalidades](#3-funcionalidades) que el proyecto pide del interfaz.
+
+#### Pruebas unitarias
+
+El _boilerplate_ de este proyecto no incluye Pruebas Unitarias (_tests_), así es
+que tendrás que escribirlas tú para las funciones encargadas de _procesar_,
+_filtrar_ y _ordenar_ la data, así como _calcular_ estadísticas. Este proyecto usa
+el framework [Jest](https://jestjs.io/) para ejecutar las pruebas unitarias por lo
+que te recomendamos consultar su documentación.
+
+Tus _pruebas unitarias_ deben dar una cobertura del 70% de _statements_
+(_sentencias_), _functions_ (_funciones_), _lines_ (_líneas_), y _branches_
+(_ramas_) del archivo `src/dataFunctions.js` que contenga tus funciones y
+está detallado en la sección de [Consideraciones técnicas](#src/data.js).
+
+## 8. Hacker edition
+
+Las secciones llamadas _Hacker Edition_ son **opcionales**. Si **terminaste**
+con todo lo anterior y te queda tiempo, intenta completarlas. Así podrás
+profundizar y/o ejercitar más sobre los objetivos de
+aprendizaje del proyecto.
+
+Features/características extra sugeridas:
+
+* Visualizar la estadística calculada mediante un gráfico. Para
+ ello te recomendamos explorar librerías de gráficas como
+ [Chart.js](https://www.chartjs.org/)
+ o [Google Charts](https://developers.google.com/chart/).
+* 100% Coverage
+
+## 9. Objetivos de aprendizaje
+
+
+Reflexiona y luego marca los objetivos que has llegado a entender y aplicar en tu proyecto. Piensa en eso al decidir tu estrategia de trabajo.
+
+### HTML
+
+- [ ] **Uso de HTML semántico**
+
+ Links
+
+ * [HTML semántico](https://curriculum.laboratoria.la/es/topics/html/html5/semantic-html)
+ * [Semantics - MDN Web Docs Glossary](https://developer.mozilla.org/en-US/docs/Glossary/Semantics#Semantics_in_HTML)
+
+
+### CSS
+
+- [ ] **Uso de selectores de CSS**
+
+ Links
+
+ * [Intro a CSS](https://curriculum.laboratoria.la/es/topics/css/css/intro-css)
+ * [CSS Selectors - MDN](https://developer.mozilla.org/es/docs/Web/CSS/CSS_Selectors)
+
+
+- [ ] **Modelo de caja (box model): borde, margen, padding**
+
+ Links
+
+ * [Box Model & Display](https://curriculum.laboratoria.la/es/topics/css/css/boxmodel-and-display)
+ * [The box model - MDN](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/The_box_model)
+ * [Introduction to the CSS box model - MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Box_Model/Introduction_to_the_CSS_box_model)
+ * [CSS display - MDN](https://developer.mozilla.org/pt-BR/docs/Web/CSS/display)
+ * [display - CSS Tricks](https://css-tricks.com/almanac/properties/d/display/)
+
+
+- [ ] **Uso de flexbox en CSS**
+
+ Links
+
+ * [A Complete Guide to Flexbox - CSS Tricks](https://css-tricks.com/snippets/css/a-guide-to-flexbox/)
+ * [Flexbox Froggy](https://flexboxfroggy.com/#es)
+ * [Flexbox - MDN](https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Flexbox)
+
+
+### Web APIs
+
+#### DOM (Document Object Model)
+
+- [ ] **Uso de selectores del DOM**
+
+ Links
+
+ * [Manipulación del DOM](https://curriculum.laboratoria.la/es/topics/browser/dom/1-dom-methods-selection)
+ * [Introducción al DOM - MDN](https://developer.mozilla.org/es/docs/Web/API/Document_Object_Model/Introduction)
+ * [Localizando elementos DOM usando selectores - MDN](https://developer.mozilla.org/es/docs/Web/API/Document_object_model/Locating_DOM_elements_using_selectors)
+
+
+- [ ] **Manejo de eventos del DOM (listeners, propagación, delegación)**
+
+ Links
+
+ * [Introducción a eventos - MDN](https://developer.mozilla.org/es/docs/Learn/JavaScript/Building_blocks/Events)
+ * [EventTarget.addEventListener() - MDN](https://developer.mozilla.org/es/docs/Web/API/EventTarget/addEventListener)
+ * [EventTarget.removeEventListener() - MDN](https://developer.mozilla.org/es/docs/Web/API/EventTarget/removeEventListener)
+ * [El objeto Event](https://developer.mozilla.org/es/docs/Web/API/Event)
+
+
+- [ ] **Manipulación dinámica del DOM**
+
+ Links
+
+ * [Introducción al DOM](https://developer.mozilla.org/es/docs/Web/API/Document_Object_Model/Introduction)
+ * [Node.appendChild() - MDN](https://developer.mozilla.org/es/docs/Web/API/Node/appendChild)
+ * [Document.createElement() - MDN](https://developer.mozilla.org/es/docs/Web/API/Document/createElement)
+ * [Document.createTextNode()](https://developer.mozilla.org/es/docs/Web/API/Document/createTextNode)
+ * [Element.innerHTML - MDN](https://developer.mozilla.org/es/docs/Web/API/Element/innerHTML)
+ * [Node.textContent - MDN](https://developer.mozilla.org/es/docs/Web/API/Node/textContent)
+
+
+### JavaScript
+
+- [ ] **Variables (declaración, asignación, ámbito)**
+
+ Links
+
+ * [Valores, tipos de datos y operadores](https://curriculum.laboratoria.la/es/topics/javascript/basics/values-variables-and-types)
+ * [Variables](https://curriculum.laboratoria.la/es/topics/javascript/basics/variables)
+
+
+- [ ] **Uso de condicionales (if-else, switch, operador ternario, lógica booleana)**
+
+ Links
+
+ * [Estructuras condicionales y repetitivas](https://curriculum.laboratoria.la/es/topics/javascript/flow-control/conditionals-and-loops)
+ * [Tomando decisiones en tu código — condicionales - MDN](https://developer.mozilla.org/es/docs/Learn/JavaScript/Building_blocks/conditionals)
+
+
+- [ ] **Uso de bucles/ciclos (while, for, for..of)**
+
+ Links
+
+ * [Bucles (Loops)](https://curriculum.laboratoria.la/es/topics/javascript/flow-control/loops)
+ * [Bucles e iteración - MDN](https://developer.mozilla.org/es/docs/Web/JavaScript/Guide/Loops_and_iteration)
+
+
+- [ ] **Funciones (params, args, return)**
+
+ Links
+
+ * [Funciones (control de flujo)](https://curriculum.laboratoria.la/es/topics/javascript/flow-control/functions)
+ * [Funciones clásicas](https://curriculum.laboratoria.la/es/topics/javascript/functions/classic)
+ * [Arrow Functions](https://curriculum.laboratoria.la/es/topics/javascript/functions/arrow)
+ * [Funciones — bloques de código reutilizables - MDN](https://developer.mozilla.org/es/docs/Learn/JavaScript/Building_blocks/Functions)
+
+
+- [ ] **Uso de linter (ESLINT)**
+
+- [ ] **Uso de identificadores descriptivos (Nomenclatura y Semántica)**
+
+- [ ] **Diferenciar entre expresiones (expressions) y sentencias (statements)**
+
+#### Tipos de datos
+
+- [ ] **Diferenciar entre tipos de datos primitivos y no primitivos**
+
+- [ ] **Arrays (arreglos)**
+
+ Links
+
+ * [Arreglos](https://curriculum.laboratoria.la/es/topics/javascript/arrays)
+ * [Array - MDN](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Array/)
+ * [Array.prototype.sort() - MDN](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)
+ * [Array.prototype.forEach() - MDN](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach)
+ * [Array.prototype.map() - MDN](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Array/map)
+ * [Array.prototype.filter() - MDN](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Array/filter)
+ * [Array.prototype.reduce() - MDN](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce)
+
+
+- [ ] **Objetos (key, value)**
+
+ Links
+
+ * [Objetos en JavaScript](https://curriculum.laboratoria.la/es/topics/javascript/objects/objects)
+
+
+#### Testing en Javascript
+
+- [ ] **Pruebas unitarias (unit tests)**
+
+ Links
+
+ * [Empezando con Jest - Documentación oficial](https://jestjs.io/docs/es-ES/getting-started)
+
+
+#### Módulos
+
+- [ ] **Módulos de ECMAScript (ES Modules)**
+
+ Links
+
+ * [import - MDN](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Statements/import)
+ * [export - MDN](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Statements/export)
+
+
+### Control de Versiones (Git y GitHub)
+
+#### Git
+
+- [ ] **Git: Instalación y configuración**
+
+- [ ] **Git: Control de versiones con git (init, clone, add, commit, status, push, pull, remote)**
+
+- [ ] **Git: Integración de cambios entre ramas (branch, checkout, fetch, merge, reset, rebase, tag)**
+
+#### GitHub
+
+- [ ] **GitHub: Creación de cuenta y repos, configuración de llaves SSH**
+
+- [ ] **GitHub: Despliegue con GitHub Pages**
+
+ Links
+
+ * [Sitio oficial de GitHub Pages](https://pages.github.com/)
+
+
+- [ ] **GitHub: Colaboración en Github (branches | forks | pull requests | code review | tags)**
+
+### Centrado en el usuario
+
+- [ ] **Diseñar y desarrollar un producto o servicio poniendo a las usuarias en el centro**
+
+### Diseño de producto
+
+- [ ] **Crear prototipos de alta fidelidad que incluyan interacciones**
+
+- [ ] **Seguir los principios básicos de diseño visual**
+
+### Investigación
+
+- [ ] **Planear y ejecutar testeos de usabilidad de prototipos en distintos niveles de fidelidad**
+
+ Links
+
+ * [Intro a testeos usabilidad](https://coda.io/@bootcamp-laboratoria/contenido-ux/test-de-usabilidad-15)
+ * [Pruebas con Usuarios 1 — ¿Qué, cuándo y para qué testeamos?](https://eugeniacasabona.medium.com/pruebas-con-usuarios-1-qu%C3%A9-cu%C3%A1ndo-y-para-qu%C3%A9-testeamos-7c3a89b4b5e7)
+
+
+### AI Prompting
+
+- [ ] **Dando Instrucciones**
+
+ Links
+
+ * [Dando Instrucciones | Learn Prompting: Your Guide to Communicating with AI](https://learnprompting.org/es/docs/basics/instructions)
+
+
+- [ ] **Few shot prompting**
+
+ Links
+
+ * [Few shot prompting | Learn Prompting: Your Guide to Communicating with AI](https://learnprompting.org/es/docs/basics/few_shot)
+
+
+## 10. Pistas, tips y lecturas complementarias
+
+### Primeros pasos
+
+Súmate al canal de Slack
+[#project-dataverse](https://claseslaboratoria.slack.com/archives/C03MV35EP5M)
+para conversar y pedir ayuda del proyecto.
+
+Antes de empezar a escribir código, debes definir qué deberá hacer el
+producto con base en el conocimiento que puedas obtener de tu usuaria.
+Estas preguntas te pueden ayudar:
+
+* ¿Quiénes son las principales usuarias del producto?
+* ¿Cuáles son los objetivos de estas usuarias en relación con el producto?
+* ¿Cuáles son los datos más relevantes que quieren ver en la interfaz y por qué?
+* ¿Cuándo utilizan o utilizarían el producto?
+* Toda tu investigación previa debe tener como resultado todas las Historias
+ de Usuaria de tu proyecto.
+* No hagas los prototipos de alta fidelidad de todas tus Historias. Comienza
+ solamente por los que se necesiten para tu Sprint 1
+ (semana 1 de trabajo). Más
+ pistas en la guía de organización para el proyecto.
+
+Cuando ya estés lista para codear, te sugerimos empezar de esta manera:
+
+1. Una de las integrantes del equipo debe realizar un :fork_and_knife:
+ [fork](https://help.github.com/articles/fork-a-repo/) del repo de tu cohort,
+ tus _coaches_ te compartirán un _link_ a un repo y te darán acceso de lectura
+ en ese repo. La otra integrante del equipo deber hacer un fork **del
+ repositorio de su compañera** y
+ [configurar](https://gist.github.com/BCasal/026e4c7f5c71418485c1) un `remote`
+ hacia el mismo.
+2. :arrow_down: [Clona](https://help.github.com/articles/cloning-a-repository/)
+ tu _fork_ a tu computadora (copia local).
+3. 📦 Instala las dependencias del proyecto con el comando `npm install`. Esto
+ asume que has instalado [Node.js](https://nodejs.org/) (que incluye [npm](https://docs.npmjs.com/)).
+4. Si todo ha ido bien, deberías poder ejecutar las :traffic_light:
+ pruebas unitarias (unit tests) con el comando `npm test`.
+5. Para ver la interfaz de tu programa en el navegador, usa el comando
+ `npm start` para arrancar el servidor web y dirígete a
+ `http://localhost:5000` en tu navegador.
+6. A codear se ha dicho! :rocket:
+
+### Contenido de referencia
+
+#### Desarrollo Front-end
+
+* [Tópicos en la currícula de Laboratoria](https://curriculum.laboratoria.la/es/web-dev/topics)
+ testing, arreglos, objetos, funciones, DOM en Browser Javascript.
+* [Buscando elementos con querySelector*](https://es.javascript.info/searching-elements-dom)
+* [Objeto del evento](https://es.javascript.info/introduction-browser-events#objeto-del-evento)
+* [Array en MDN](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Array)
+* [Array.sort en MDN](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Array/sort)
+* [Array.toSorted en MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toSorted)
+* [Array.map en MDN](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Array/map)
+* [Array.filter en MDN](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Array/filter)
+* [Array.reduce en MDN](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Array/reduce)
+* [Array.forEach en MDN](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Array/forEach)
+* [Object.keys en MDN](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Object/keys)
+* [Object.entries en MDN](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Object/entries)
+* [Atributos de datos](https://developer.mozilla.org/es/docs/Learn/HTML/Howto/Use_data_attributes)
+* [expressions-vs-statements](https://2ality.com/2012/09/expressions-vs-statements.html)
+* [expresión vs sentencia](https://openclassrooms.com/en/courses/4309531-descubre-las-funciones-en-javascript/5108986-diferencia-entre-expresion-y-sentencia)
+* [Datos atómicos vs datos estructurados](https://www.todojs.com/tipos-datos-javascript-es6/)
+* [Módulos: Export](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Sentencias/export)
+* [Módulos: Import](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Sentencias/import)
+* [Diferencia entre array y objetos](https://youtu.be/mJJloQY7A8Y)
+* [¿Cómo puedo recorrer un objeto?](https://youtube.com/01RHn23Bn_0)
+* [`map`, `filter`, `sort` y `reduce` también son métodos para objetos](https://youtu.be/bUl1R2lQvKo)
+* [Diferencia entre expression y statements](https://youtu.be/wlukoWco2zk)
+* [Diferencia entre createElement e innerHTML](https://www.javascripttutorial.net/javascript-dom/javascript-innerhtml-vs-createelement/)
+* [¿Qué es el Scope?](https://youtu.be/s-7C09ymzK8)
+
+#### Herramientas
+
+* [Git](https://git-scm.com/)
+* [GitHub](https://github.com/)
+* [GitHub Pages](https://pages.github.com/)
+* [Para preguntas sobre Git recomendamos ver este playlist](https://www.youtube.com/watch?v=F1EoBbvhaqU&list=PLiAEe0-R7u8k9o3PbT3_QdyoBW_RX8rnV)
+* [Node.js](https://nodejs.org/)
+* [Jest](https://jestjs.io/)
+
+#### Organización del Trabajo
+
+* [Historias de Usuario](https://www.youtube.com/watch?v=ky6wFiF5vMk&t=344s).
+ Ojo que Cris no diferencia _Definición de terminado_ de _Criterios de
+ Aceptación_ y nosotros sí lo haremos. Más detalles en la guía.
+* [Cómo dividir H.U.](https://www.youtube.com/watch?v=Ueq786iZ30I&t=341s)
+* [Qué es Pair Programming?](https://www.youtube.com/watch?v=T7xQnA2Qpak&feature=youtu.be)
+
+## 11. Consideraciones para pedir tu Project Feedback
+
+Antes de agendar tu Project Feedback con un coach:
+
+* Cumple con los criterios mínimos de aceptación al ejecutar `npm run test:oas`
+* Cumple con las pruebas unitarias al ejecutar `npm run test` y
+ que tienen una cobertura del 70% de _statements_ (_sentencias_),
+ _functions_ (_funciones_), _lines_ (_líneas_), y _branches_
+* Cumple con las pruebas _end to end_ al ejecutar `npm run test:e2e`
+
+No es necesario que todas las pruebas
+pasen con 100% para poder tener tu Project Feedback.
+El objetivo de los tests no es bloquearte, pero es importante que comprendas
+qué objetivos tienes pendientes y discutas con tu coach si alguno
+de ellos es crucial para lograrlo antes de tu Project Feedback.
+
+A continuación, antes de tu Project Feedback con un coach asegúrate que
+tu proyecto:
+
+* Esta libre de _errores_ de `eslint` al ejecutar `npm run pretest`
+* Está subido a GitHub y desplegado en GitHub Pages
+* Captura de pantalla del prompt utilizado para generar los datos.
+* Tiene un `README.md` con la siguiente:
+ - _Definición del producto_ clara e informativa
+ - Historias de usuario
+ - Un _Diseño de la Interfaz de Usuaria_ (prototipo de alta fidelidad)
+ - El listado de problemas que detectaste a través de tests
+ de usabilidad en el `README.md`
+* Tiene un UI que cumple las funcionalidades:
+ - Muestra lista con datos y/o indicadores
+ - Permite ordenar data por uno o más campos (asc y desc)
+ - Permite filtrar data con base en una condición
+ - Permite limpiar los filtros con un botón
+ - Es _responsive_
+
+Recuerda que debes hacer una autoevaluación de _objetivos de aprendizaje_ y
+_life skills_ desde tu dashboard de estudiante.
diff --git a/docs/01-milestone.md b/docs/01-milestone.md
new file mode 100644
index 00000000..1711ea29
--- /dev/null
+++ b/docs/01-milestone.md
@@ -0,0 +1,83 @@
+# **HITO 1:** Data y diseño
+
+Bienvenida al primer hito de tu aprendizaje en JavaScript mediante proyectos.
+En esta fase, comenzarás construyendo una aplicación centrada en la
+generación de datos mediante inteligencia artificial.
+
+## Tareas de este hito
+
+- [Prototipo](#prototipo)
+- [Creación de set de datos](#creación-de-set-de-datos)
+- [Test de prompting](#test-de-prompting)
+
+### Prototipo
+
+Elabora el diseño de la interfaz de la página principal,
+donde se exhiben los elementos de tu conjunto de datos.
+
+Es esencial que tu propuesta visual no solo represente la esencia
+de tu solución ideal, sino que también se ajuste rigurosamente a
+los principios fundamentales del
+[diseño visual](https://coda.io/d/Bootcamp-UX-Contenido_dqkqk2rV9Z2/Diseno-de-interfaces_suOT7#_luWsQ).
+
+### Creación de set de datos
+
+El objetivo es crear un conjunto de datos que cumpla con las
+[especificaciones proporcionadas](../README.md/#generar-los-datos).
+
+1. **Definir temática**: En equipo seleccionen una temática para el conjunto de datos,
+por ejemplo, personajes históricos, y tener en cuenta que la información sea variada
+e interesante para la interacción futura.
+
+2. **Estructurar datos**: En el archivo `src/data/dataset.js,` al generar tus datos,
+reemplaza el contenido con la información generada. Asegúrate de que el archivo
+exporte un arreglo con 24 objetos, cada uno siguiendo la estructura detallada
+en el
+[prompt](https://espresso-matutino.notion.site/6-Pasos-Para-El-Prompt-Perfecto-280cac492ab54a258771ec56de27807d).
+Verifica que cada objeto contenga un **id** (identificador único),
+**name** (nombre), **shortDescription** (descripción corta) y
+**description** (descripción extendida), **imageUrl** (para la imagen),
+**facts** (hechos), y,
+opcionalmente, **extraInfo** (información adicional).
+
+3. **Añadir imágenes**: Utilizar herramientas basadas en inteligencia artificial,
+como el generador de imágenes de Bing, para crear imágenes correspondientes a cada
+elemento del conjunto de datos o sencillamente googlearlas y obtener sus URLs.
+
+4. **Datos de ejemplo**: En caso de no lograr generar los datos en un
+periodo breve, como máximo tres días, puedes recurrir a los datos de
+ejemplo disponibles en la ruta `./src/data/dataset.js`
+**Es crucial no dedicar un sprint completo exclusivamente
+a esta tarea**.
+
+5. **Capturas de pantalla**: Incluir capturas de pantalla del prompt utilizado
+para la generación de datos en el archivo readme, cumpliendo con el
+requisito del proyecto.
+
+### Test de prompting
+
+Asegúrate de verificar con los tests ejecutando `npm run test:oas-prompting` para
+garantizar que el archivo de datos generado cumple con los requisitos especificados.
+
+![Preview oas prompting](https://github.com/Laboratoria/curriculum/assets/39414582/58f383ec-0b61-45de-b848-b3380b7a8d1e)
+
+## ¿Necesitas Ayuda?
+
+En caso de que surjan dudas o preguntas durante la ejecución de este proyecto,
+no dudes en buscar ayuda. Puedes encontrar apoyo a través de:
+
+- **Documentación del Proyecto:** Asegúrate de revisar minuciosamente la
+información proporcionada en el [README](../README.md), prestando especial
+atención a las indicaciones, sugerencias y lecturas adicionales.
+
+- **Coaches:** Los coaches están disponibles para brindarte ayuda.
+Si encuentras desafíos que la documentación no aborda o si necesitas
+una explicación más detallada, no dudes en solicitar una reunión de orientación
+(OH) o comunicarte con nosotros a través de Slack.
+
+- **Slack:** Siempre puedes escribir en el canal del proyecto
+[#project-dataverse](https://claseslaboratoria.slack.com/archives/C05V648LL1G),
+donde tus compañeras y coaches de Laboratoria pueden brindarte
+apoyo de manera asincrónica.
+
+[👈Todos los hitos](../README.md#6-hitos)
diff --git a/docs/02-milestone.md b/docs/02-milestone.md
new file mode 100644
index 00000000..b66369c9
--- /dev/null
+++ b/docs/02-milestone.md
@@ -0,0 +1,104 @@
+# **HITO 2:** Visualización de datos
+
+A partir de este momento, es fundamental que
+trabajes en cada hito utilizando una
+[rama](https://www.atlassian.com/es/git/tutorials/comparing-workflows/feature-branch-workflow)
+individual para facilitar la claridad, coherencia y colaboración.
+El objetivo de este hito es lograr la visualización de
+los elementos de tu conjunto de datos con todos los estilos.
+
+## Tareas de este hito
+
+- [Visualizar](#visualizar)
+- [Estilos](#estilos)
+- [Test](#test)
+
+### Visualizar
+
+> [!IMPORTANT]
+> Para visualizar los datos, es crucial que repases
+> los conceptos de
+> [parámetros y argumentos](https://www.youtube.com/watch?v=5VVBrfWQ2Wk)
+> de una función, ya que los utilizarás extensivamente.
+
+En el archivo `src/main.js`, observarás que las primeras líneas
+están ocupadas por las importaciones de la `data` y de `renderItems`.
+En este archivo, llama a `renderItems` para enviar la data como argumento.
+
+```js
+import { example } from './dataFunctions.js';
+import { renderItems } from './view.js';
+
+import data from './data/dataset.js';
+
+console.log(example, renderItems(data), data);
+
+// Invocación de renderItems
+renderItems(data)
+```
+
+Ahora en el archivo `src/view.js`, existe la función `renderItems`,
+la cual tiene la responsabilidad de:
+
+1. Recibir el parámetro data, que representa todo el conjunto de datos.
+2. Crear un elemento `` utilizando el método
+[createElement](https://developer.mozilla.org/es/docs/Web/API/Document/createElement).
+3. Recorrer la data mediante un
+[bucle](https://developer.mozilla.org/es/docs/Web/JavaScript/Guide/Loops_and_iteration)
+o algún método de array como
+[forEach](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach)
+o
+[map](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Array/map)
+4. Por cada elemento de la data, crear un elemento ``.
+5. Agregar cada ` ` al `` utilizando
+[innerHTML](https://developer.mozilla.org/es/docs/Web/API/Element/innerHTML) ,
+[appendChild](https://developer.mozilla.org/es/docs/Web/API/Node/appendChild)
+u otro método apropiado.
+6. Finalmente, retornar el elemento ``.
+
+Hasta este punto, aún no se visualiza la data, pero puedes utilizar
+`console.log` en todo momento. Por ejemplo, dentro del bucle o método que
+elijas, para verificar la iteración.
+
+Para concluir esta fase, debes seleccionar el elemento HTML mediante un
+[selector del DOM](https://developer.mozilla.org/es/docs/Web/API/Document_object_model/Locating_DOM_elements_using_selectors).
+Posteriormente, indica dónde se debe colocar el resultado de la invocación de
+renderItems, recurriendo nuevamente a
+[innerHTML](https://developer.mozilla.org/es/docs/Web/API/Element/innerHTML) o
+[appendChild](https://developer.mozilla.org/es/docs/Web/API/Node/appendChild)
+
+> [!NOTE]
+> **appendChild()**: Este método agrega un elemento hijo al final
+> de un elemento padre.
+> **innerHTML**: Esta propiedad asigna el contenido de un elemento
+> HTML a otro elemento.
+
+### Estilos
+
+Para crear una cuadrícula adaptable a diferentes dispositivos
+con Flexbox, utiliza la propiedad `flex` y ten en cuenta el uso
+de `wrap` para manejar múltiples filas o columnas.
+
+Recursos utiles:
+
+- [Flexbox](https://curriculum.laboratoria.la/es/topics/css/css/flexbox)
+- [Flexbox Froggy](https://flexboxfroggy.com/#es)
+- [CSS Tricks](https://css-tricks.com/snippets/css/a-guide-to-flexbox/)
+
+### Test
+
+Verifica qué está sucediendo con los tests;
+este es un buen momento para ejecutarlos y ajustar
+las partes del código que sean necesarias. Aunque
+es posible que varios tests aún no pasen, es
+fundamental abordar cualquier problema identificado
+para garantizar la robustez y precisión del código.
+
+``` sh
+npm run test:oas-html
+npm run test:oas-css
+npm run test:oas-web-api
+npm run test:oas-js
+```
+
+[👈Todos los hitos](../README.md#6-hitos)
diff --git a/docs/03-milestone.md b/docs/03-milestone.md
new file mode 100644
index 00000000..5bb8680a
--- /dev/null
+++ b/docs/03-milestone.md
@@ -0,0 +1,150 @@
+# **HITO 3:** Filtrar y ordenar datos
+
+Este hito tiene como objetivo lograr la filtración y
+ordenación de la data. No olvides que es esencial seguir
+la práctica de utilizar al menos una
+[rama](https://www.atlassian.com/es/git/tutorials/comparing-workflows/feature-branch-workflow)
+por hito o por característica.
+
+## Tareas de este hito
+
+- [Filtrar datos del dataset](#filtrar-datos-del-dataset)
+- [Test de la función del filtro](#test-de-la-función-del-filtro)
+- [Ordenar datos del dataset](#ordenar-datos-del-dataset)
+- [Test de la función ordenar](#test-de-la-función-ordenar)
+- [Test OAS](#test-oas)
+
+### Filtrar datos del dataset
+
+Como primer paso, vuelve las secciónes
+[4. Funcionalidades](../README.md/#4-funcionalidades),
+[5. Consideraciones técnicas](../README.md/#5-consideraciones-técnicas) y
+[src/dataFunctions.js](../README.md/#srcdatafunctionsjs)
+para obtener detalles sobre los elementos que debes utilizar y
+el nombre que debe tener la función para el filtro.
+
+> [!TIP]
+> [filter](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Array/filter)
+
+1. En el archivo HTML, crea un elemento ``
+que incluya los valores a ser utilizados para el filtrado.
+2. En el archivo principal `src/main.js`, emplea algún selector
+del DOM y manejador de eventos para seleccionar y vincular un
+evento al elemento ``. Esto permitirá capturar el valor
+seleccionado en el `` de manera adecuada.
+3. En `src/dataFunctions.js`, elimina el contenido de ejemplo
+y crea la función `filterData(data, filterBy, value)` que se
+encargará de realizar el filtrado de datos.
+4. En `src/main.js`, invoca la función `filterData` y proporciona
+como argumentos la `data`, el campo por el cual deseas realizar el
+filtrado `filterBy`, y el valor específico `value` que deseas
+filtrar.
+5. Regresa a `src/dataFunctions.js` y verifica que tu función esté
+recibiendo los valores; puedes hacerlo mediante un `console.log`.
+6. Una vez que hayas confirmado los valores ejecuta el método
+`filter` y retorna el nuevo arreglo filtrado.
+7. Una vez obtenido el valor de retorno en `src/main.js`,
+llama a la función encargada de mostrar la data `renderItems`,
+pasando como argumento el nuevo arreglo ya filtrado.
+8. [Aplica estilos al `select` recién creado](#estilos)
+
+> [!IMPORTANT]
+> **No te limites a este ejemplo**. Puedes crear
+> una interfaz que te permita filtrar por diferentes
+> propiedades o con diferentes elementos. Explora
+> tu creatividad y descubre nuevas formas
+> de organizar tus datos.
+> ¡El poder está en tus manos!
+>
+
+### Test de la función del filtro
+
+¡No dejes las pruebas para el final! Ahora que tu función
+de filtrado está en funcionamiento, es el momento ideal
+para realizar pruebas. Puedes consultar la siguiente
+[lista de reproducción en YouTube](https://www.youtube.com/watch?v=gsTfbwfVvDE&list=PLiAEe0-R7u8kqvibxkK9tqqoJXnhgtefg)
+para aprender sobre pruebas y ver ejemplos con Jest.
+
+### Ordenar datos del dataset
+
+> [!IMPORTANT]
+> Aquí proporcionamos instrucciones para ordenar
+> datos basándonos en una propiedad específica,
+> pero es importante destacar que tienes la
+> libertad de agregar una interfaz de usuaria que
+> permita el ordenamiento por diversas propiedades.
+> **Este es solo un ejemplo, no la única técnica posible.**
+>
+
+La función de ordenar `sortData` debe recibir datos, un campo de
+referencia y la dirección del orden. Luego aplica el ordenamiento
+con `sort` y devuelve el nuevo arreglo. Esto permite ordenar
+dinámicamente la data según criterios específicos.
+
+1. En el archivo `HTML`, crea un elemento `` con opciones
+`` para ordenar datos ascendente ("asc") y descendente ("desc").
+2. En `src/main.js`, utiliza un selector del DOM y un manejador
+de eventos para vincular el `` y capturar eficientemente
+el valor seleccionado.
+3. En `src/dataFunctions.js`, `crea sortData(data, sortBy, sortOrder)`
+para ordenar datos según los parámetros especificados.
+4. En `src/main.js`, llama a `sortData` con la `data`,
+el campo para ordenar `sortBy`, y la dirección del orden
+`sortOrder` (ascendente o descendente).
+5. En `src/dataFunctions.js`, verifica la función
+con `console.log` para confirmar los valores.
+6. Realiza una copia de data, ejecuta el método `sort`
+y retorna el nuevo arreglo ordenado.
+7. En `src/main.js`, usa el resultado en
+`renderItems` para mostrar la data ordenada.
+8. [Aplica estilos al `select` recién creado](#estilos)
+
+> [!TIP]
+> [sort](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Array/sort).
+> Hacer una copia del
+> array antes de ordenarlo en JavaScript es crucial para
+> evitar modificaciones no deseadas. El método `sort()`
+> altera directamente el array original, por lo que crear
+> una copia previa garantiza la preservación del estado
+> original del array.
+
+### Test de la función ordenar
+
+Realiza los test; ahora que tienes la función de
+ordenar funcionando, escribe los tests. Puedes usar esta
+[lista de reproducción en YouTube](https://www.youtube.com/watch?v=gsTfbwfVvDE&list=PLiAEe0-R7u8kqvibxkK9tqqoJXnhgtefg)
+como referencia.
+
+### Estilos
+
+Asegúrate de posicionarlo adecuadamente, establecer un
+[tamaño estándar](https://www.w3.org/TR/WCAG21/#target-size)
+para que sea fácilmente pulsables y diferenciar
+visualmente el llamado a la acción
+[CTA](https://www.40defiebre.com/que-es/call-to-action).
+Puedes utilizar propiedades como `width`, `height`, `margin`,
+`padding`, y `font-size` para ajustar el tamaño y el
+espacio, y `border`, `background-color`, y `color`
+para darle un aspecto atractivo. Considera también la
+posibilidad de utilizar estilos adicionales como sombras, bordes
+redondeados para mejorar la apariencia y usabilidad o estilos
+específicos para los estados `hover` y `focus` para mejorar la
+interactividad
+
+### Test OAS
+
+Comprueba el estado de los tests;
+este es un momento propicio para ejecutarlos
+y realizar ajustes en aquellas secciones del
+código que sean necesarios. Aunque es probable que algunos tests aún no den resultado
+positivo, es crucial abordar cualquier problema
+identificado con el fin de asegurar la solidez y exactitud del código.
+
+``` sh
+npm run test:oas-html
+npm run test:oas-css
+npm run test:oas-web-api
+npm run test:oas-js
+```
+
+[👈Todos los hitos](../README.md#6-hitos)
diff --git a/docs/04-milestone.md b/docs/04-milestone.md
new file mode 100644
index 00000000..1c7bb990
--- /dev/null
+++ b/docs/04-milestone.md
@@ -0,0 +1,81 @@
+# **HITO 4:** Cálculo
+
+El propósito de este hito es realizar cálculos con
+la información. Recuerda la importancia de seguir
+la práctica de
+emplear al menos una
+[rama](https://www.atlassian.com/es/git/tutorials/comparing-workflows/feature-branch-workflow)
+por hito o característica.
+
+## Tareas de este hito
+
+- [Cálculos estadísticos del dataset](#cálculos-estadísticos-del-dataset)
+- [Test de la función del calcular](#test-de-la-función-del-calcular)
+- [Test OAS](#test-oas)
+- [Hacker edition](#hacker-edition)
+
+### Cálculos estadísticos del dataset
+
+La función `computeStats` debe recibir la `data` y realizar
+cálculos según los criterios que estás estableciendo.
+
+1. Selecciona el tipo de cálculo que deseas realizar y
+evalúa la necesidad de un activador, como un botón o
+un menú desplegable, para llevar a cabo la operación.
+Asegúrate de considerar la experiencia de usuaria
+y el contexto de la aplicación al tomar esta decisión.
+2. En el archivo principal `src/main.js`, utiliza un
+selector del DOM y un manejador de eventos para
+seleccionar y vincular un evento al elemento `` o ``.
+3. En `src/dataFunctions.js`, crea la función `computeStats(data)`
+encargada de realizar los cálculos según el criterio.
+4. En `src/main.js`, invoca la función `computeStats`
+y proporciona la `data` como argumento para realizar los
+cálculos necesarios.
+5. Regresa a `src/dataFunctions.js` y verifica que la
+función esté recibiendo los valores;
+puedes hacerlo mediante un `console.log`.
+6. Una vez confirmados los valores, realiza los cálculos necesarios,
+por ejemplo la longitud promedio de los nombres, usando el metodo
+[reduce](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce)
+y devuelve el resultado.
+7. Presenta en tu interfaz los resultados del calculo.
+
+> [!TIP]
+> [reduce](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce)
+
+### Test de la función del calcular
+
+Realiza las pruebas; ahora que has implementado
+la función de cálculo, procede a escribir los tests.
+Puedes utilizar esta
+[lista de reproducción en YouTube](https://www.youtube.com/watch?v=gsTfbwfVvDE&list=PLiAEe0-R7u8kqvibxkK9tqqoJXnhgtefg)
+como punto de referencia.
+
+### Test OAS
+
+En este punto, la verificación constante de los tests debe formar
+parte de tu rutina diaria. Ejecuta las pruebas y realiza los cambios
+o ajustes necesarios para que todas y cada una de ellas pasen
+satisfactoriamente. Este proceso continuo garantiza la calidad
+y fiabilidad del código a lo largo del desarrollo del proyecto.
+
+``` sh
+npm run test:oas-html
+npm run test:oas-css
+npm run test:oas-web-api
+npm run test:oas-js
+```
+
+### Hacker edition
+
+Antes de embarcarte en la sección de "Hacker Edition", es
+crucial que valores tu disponibilidad de tiempo. No es aconsejable
+prolongar la ejecución de un proyecto más allá de lo necesario,
+ya que el bootcamp tiene un límite de tiempo establecido, y quedarse
+indefinidamente en el mismo proyecto no es viable. Siempre puedes
+consultar esta decisión con tus coaches para obtener orientación adicional.
+
+[Hacker Edition](../README.md/#8-hacker-edition)
+
+[👈Todos los hitos](../README.md#6-hitos)
diff --git a/explaindev.json b/explaindev.json
new file mode 100644
index 00000000..ce2be426
--- /dev/null
+++ b/explaindev.json
@@ -0,0 +1,4 @@
+{
+ "project": "dataverse",
+ "cohort": "DEV015"
+}
\ No newline at end of file
diff --git a/package.json b/package.json
new file mode 100644
index 00000000..a0949bd6
--- /dev/null
+++ b/package.json
@@ -0,0 +1,52 @@
+{
+ "name": "dataverse",
+ "version": "1.0.0",
+ "main": "src/index.html",
+ "license": "MIT",
+ "scripts": {
+ "htmlhint": "htmlhint src/*.html test/*.html",
+ "eslint": "eslint --ext .js src/ test/",
+ "pretest": "npm run eslint && npm run htmlhint",
+ "test": "jest --verbose --coverage test/",
+ "test:e2e": "playwright test",
+ "test:oas": "jest --verbose tests-read-only/oa/",
+ "pretest:oas-html": "npm run pretest",
+ "test:oas-html": "jest --verbose tests-read-only/oa/oa-html.spec.js",
+ "test:oas-css": "jest --verbose tests-read-only/oa/oa-css.spec.js",
+ "pretest:oas-js": "npm run pretest",
+ "test:oas-js": "jest --verbose tests-read-only/oa/oa-javascript.spec.js",
+ "pretest:oas-web-api": "npm run pretest",
+ "test:oas-web-api": "jest --verbose tests-read-only/oa/oa-web-api.spec.js",
+ "pretest:oas-prompting": "npm run pretest",
+ "test:oas-prompting": "jest --verbose tests-read-only/oa/oa-prompting.spec.js",
+ "open-coverage-report": "opener ./coverage/lcov-report/index.html",
+ "start": "serve src/",
+ "deploy": "gh-pages -d src"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.6.2",
+ "@babel/plugin-transform-modules-commonjs": "^7.6.0",
+ "@playwright/test": "^1.36.1",
+ "acorn": "^8.8.2",
+ "babel-jest": "^27.0.1",
+ "css": "^3.0.0",
+ "eslint": "^8.3.0",
+ "gh-pages": "^3.1.0",
+ "htmlhint": "^1.0.0",
+ "jest": "^27.0.1",
+ "jsdom": "^22.1.0",
+ "opener": "^1.5.1",
+ "serve": "^14.2.1"
+ },
+ "engines": {
+ "node": ">=16.x"
+ },
+ "bootcamp": {
+ "createdAt": "2024-05-27T17:56:17.548Z",
+ "version": "8.7.0",
+ "commit": "6bfcf3abf54cef1793fffc158785db46b9babb93"
+ },
+ "jest": {
+ "testEnvironment": "jsdom"
+ }
+}
\ No newline at end of file
diff --git a/playwright.config.js b/playwright.config.js
new file mode 100644
index 00000000..86f8e1b4
--- /dev/null
+++ b/playwright.config.js
@@ -0,0 +1,47 @@
+import { defineConfig, devices } from '@playwright/test';
+
+/**
+ * Read environment variables from file.
+ * https://github.com/motdotla/dotenv
+ */
+// require('dotenv').config();
+
+/**
+ * See https://playwright.dev/docs/test-configuration.
+ */
+export default defineConfig({
+ testDir: './tests-read-only/e2e',
+ /* Run tests in files in parallel */
+ fullyParallel: true,
+ /* Fail the build on CI if you accidentally left test.only in the source code. */
+ forbidOnly: !!process.env.CI,
+ /* Retry on CI only */
+ retries: process.env.CI ? 2 : 0,
+ /* Opt out of parallel tests on CI. */
+ workers: process.env.CI ? 1 : undefined,
+ /* Reporter to use. See https://playwright.dev/docs/test-reporters */
+ reporter: process.env.CI ? 'dot' : 'list',
+ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
+ use: {
+ /* Base URL to use in actions like `await page.goto('/')`. */
+ baseURL: 'http://localhost:3000',
+
+ /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
+ trace: 'on-first-retry',
+ },
+
+ /* Configure projects for major browsers */
+ projects: [
+ {
+ name: 'chromium',
+ use: { ...devices['Desktop Chrome'] },
+ },
+ ],
+
+ /* Run your local dev server before starting the tests */
+ webServer: {
+ command: 'npm run start',
+ url: 'http://localhost:3000',
+ reuseExistingServer: !process.env.CI,
+ },
+});
diff --git a/src/data/dataset.js b/src/data/dataset.js
new file mode 100644
index 00000000..592bf317
--- /dev/null
+++ b/src/data/dataset.js
@@ -0,0 +1,378 @@
+export default [
+ {
+ "id": "ada-lovelace",
+ "name": "Ada Lovelace",
+ "shortDescription": "Pionera en la programación, primera programadora de la historia.",
+ "description": "Una visionaria matemática británica del siglo XIX, es aclamada como la primera programadora de la historia. Su colaboración con Charles Babbage en la creación de la Máquina Analítica la llevó a desarrollar algoritmos, incluyendo el famoso \"algoritmo de Bernoulli\", destinados a ser procesados por una máquina. Su visión pionera la convirtió en una figura fundamental en la informática y la inteligencia artificial. Lovelace demostró que las computadoras podían ir más allá de simples cálculos matemáticos, anticipando un mundo donde la programación sería esencial en la resolución de problemas complejos, dejando una huella imborrable en la ciencia y la tecnología.",
+ "imageUrl": "https://laboratoria-dataverse-talks.netlify.app/public/ada-lovelace.jpg",
+ "facts": {
+ "yearOfBirth": "1815",
+ "yearOfDeath": "1852",
+ "birthPlace": "Londres, Reino Unido",
+ "mainField": "Ciencia de la Computación"
+ },
+ "extraInfo": {
+ "imageSource": "https://www.bing.com/images/create/genera-una-imagen-de-ada-lovelace2c-la-primera-prog/650a1eff8ff04b88833d5cdef0309441"
+ }
+ },
+ {
+ "id": "emmy-noether",
+ "name": "Emmy Noether",
+ "shortDescription": "Matemática destacada, teorema fundamental en álgebra abstracta.",
+ "description": "Una brillante matemática alemana, revolucionó la física teórica con sus contribuciones al teorema de conservación de la energía y el teorema de Noether, fundamentales en la teoría de la relatividad y la mecánica cuántica. A pesar de los obstáculos que enfrentó como mujer en la academia, sus investigaciones en álgebra abstracta y teoría de grupos son fundamentales en la física moderna. Su capacidad para unificar conceptos matemáticos y físicos marcó un hito en la historia de la ciencia, allanando el camino para futuras generaciones de científicas.",
+ "imageUrl": "https://laboratoria-dataverse-talks.netlify.app/public/emmy-noether.jpg",
+ "facts": {
+ "yearOfBirth": "1882",
+ "yearOfDeath": "1935",
+ "birthPlace": "Erlangen, Alemania",
+ "mainField": "Matemáticas"
+ },
+ "extraInfo": {
+ "imageSource": "https://www.bing.com/images/create/create-an-image-of-of-a-german-woman2c-a-pioneering/6510ddb1e8254b928b8f8f1d5e95fea4"
+ }
+ },
+ {
+ "id": "hypatia-de-alejandria",
+ "name": "Hypatia de Alejandría",
+ "shortDescription": "Matemática y filósofa en la antigua Alejandría.",
+ "description": "Filósofa y matemática en la antigua Alejandría, desafió las normas de género de su época al destacar en una sociedad dominada por hombres. Su legado incluye importantes contribuciones a la geometría y la astronomía, así como su destacado papel como maestra y defensora del conocimiento en una época de agitación política. Hypatia demostró que las mujeres podían sobresalir en campos intelectuales y su valentía al mantener sus creencias filosóficas la convierte en un símbolo de resistencia y sabiduría en la historia de la ciencia.",
+ "imageUrl": "https://laboratoria-dataverse-talks.netlify.app/public/hypatia-de-alejandria.jpg",
+ "facts": {
+ "yearOfBirth": "circa 360",
+ "yearOfDeath": "415",
+ "birthPlace": "Alejandría, Egipto",
+ "mainField": "Matemáticas, Filosofía"
+ },
+ "extraInfo": {
+ "imageSource": "https://www.bing.com/images/create/create-a-vivid-digital-artwork-or-painting-that-de/6510df0ebe1a4807af22175452761e1a"
+ }
+ },
+ {
+ "id": "marjorie-lee-browne",
+ "name": "Marjorie Lee Browne",
+ "shortDescription": "Matemática, contribuyó a la teoría de números.",
+ "description": "Matemática estadounidense, destacó en una época en la que las mujeres afroamericanas enfrentaban múltiples barreras en la educación superior. No solo obtuvo su doctorado en matemáticas, sino que también se convirtió en una destacada educadora, promoviendo la diversidad en las ciencias matemáticas y desafiando la discriminación racial y de género. Su compromiso con la educación y su capacidad para inspirar a futuras generaciones la convierten en un modelo a seguir para las mujeres en la ciencia, dejando una huella perdurable en la historia académica.",
+ "imageUrl": "https://laboratoria-dataverse-talks.netlify.app/public/marjorie-lee-browne.jpg",
+ "facts": {
+ "yearOfBirth": "1914",
+ "yearOfDeath": "1979",
+ "birthPlace": "Memphis, Estados Unidos",
+ "mainField": "Matemáticas"
+ },
+ "extraInfo": {
+ "imageSource": "https://www.bing.com/images/create/design-an-inspiring-and-historically-accurate-imag/6510df7e7bb745f69d67f695441607a8"
+ }
+ },
+ {
+ "id": "shakuntala-devi",
+ "name": "Shakuntala Devi",
+ "shortDescription": "Matemática prodigio, conocida como la 'Calculadora Humana'.",
+ "description": "Conocida como \"la bruja de las matemáticas\" de la India, dejó una marca indeleble en el mundo de los números. Su capacidad mental asombrosa la llevó a resolver complejos cálculos matemáticos mentalmente en cuestión de segundos, estableciendo récords mundiales. Además de su talento innato, Devi promovió activamente la educación matemática y luchó por la igualdad de género en un país donde las mujeres enfrentaban desafíos en la ciencia y la educación. Su legado destaca el poder de la mente humana y la importancia de la igualdad de oportunidades en la ciencia.",
+ "imageUrl": "https://laboratoria-dataverse-talks.netlify.app/public/shakuntala-devi.jpg",
+ "facts": {
+ "yearOfBirth": "1929",
+ "yearOfDeath": "2013",
+ "birthPlace": "Bangalore, India",
+ "mainField": "Matemáticas"
+ },
+ "extraInfo": {
+ "imageSource": "https://www.bing.com/images/create/indian-woman-writing-math-problems-in-a-blackbard-/6510e19f3db54e14b365b7e8dc5045f6"
+ }
+ },
+ {
+ "id": "sofia-kovalevskaya",
+ "name": "Sofia Kovalevskaya",
+ "shortDescription": "Matemática y escritora, contribuyó a la teoría de funciones.",
+ "description": "Matemática rusa del siglo XIX, dejó una marca indeleble en la ciencia del siglo XIX al desafiar las barreras de género. Como matemática rusa, superó las restricciones sociales de su época para convertirse en la primera mujer en obtener un doctorado en matemáticas en Europa. Sus contribuciones a la teoría de las ecuaciones diferenciales y la mecánica revolucionaron la disciplina, abriendo nuevas perspectivas en el análisis matemático. Su destacada membresía en la Academia de Ciencias de Suecia marcó un hito en la historia de las mujeres en la ciencia.",
+ "imageUrl": "https://laboratoria-dataverse-talks.netlify.app/public/sofia-kovalevskaya.jpg",
+ "facts": {
+ "yearOfBirth": "1850",
+ "yearOfDeath": "1891",
+ "birthPlace": "Moscú, Rusia",
+ "mainField": "Matemáticas"
+ },
+ "extraInfo": {
+ "imageSource": "https://www.bing.com/images/create/indian-woman-writing-math-problems-in-a-blackbard-/6510e19f3db54e14b365b7e8dc5045f6"
+ }
+ },
+ {
+ "id": "yoko-shimomura",
+ "name": "Yoko Shimomura",
+ "shortDescription": "Compositora de música, destacada en la industria de los videojuegos.",
+ "description": "Compositora japonesa, es una figura destacada en la ciencia de la música y el arte sonoro. Aunque no se dedica a la ciencia en el sentido tradicional, su trabajo en la composición musical ha requerido un profundo entendimiento de la teoría musical y la tecnología de audio. Ha contribuido de manera significativa a la industria de los videojuegos, componiendo icónicas bandas sonoras que han enriquecido la experiencia de juego. Su éxito en un campo donde las mujeres estaban subrepresentadas la convierte en un modelo a seguir, incluso en campos no convencionales como la composición musical.",
+ "imageUrl": "https://laboratoria-dataverse-talks.netlify.app/public/yoko-shimomura.jpg",
+ "facts": {
+ "yearOfBirth": "1967",
+ "birthPlace": "Hyogo, Japón",
+ "mainField": "Composición Musical, Video Juegos"
+ },
+ "extraInfo": {
+ "imageSource": "https://www.bing.com/images/create/yoko-shimomura-making-music-for-a-video-game-in-19/6510e40dd1eb45d0906e8c1f4bb601d3"
+ }
+ },
+ {
+ "id": "maryam-mirzakhani",
+ "name": "Maryam Mirzakhani",
+ "shortDescription": "Matemática, ganadora del Premio Fields.",
+ "description": "Matemática iraní, hizo historia al convertirse en la primera mujer en ganar la Medalla Fields, el premio más prestigioso en matemáticas. Sus investigaciones en la geometría de superficies y la teoría de las superficies de Riemann han tenido un impacto duradero en la matemática pura. A lo largo de su carrera, Mirzakhani inspiró a muchas mujeres a perseguir carreras en ciencia y matemáticas, derribando las barreras de género. Su enfoque innovador y su capacidad para resolver problemas complejos la convierten en un modelo a seguir para la comunidad científica y en un símbolo de la excelencia matemática.",
+ "imageUrl": "https://laboratoria-dataverse-talks.netlify.app/public/maryam-mirzakhani.jpg",
+ "facts": {
+ "yearOfBirth": "1977",
+ "yearOfDeath": "2017",
+ "birthPlace": "Teherán, Irán",
+ "mainField": "Matemáticas"
+ },
+ "extraInfo": {
+ "imageSource": "https://chalkdustmagazine.com/features/mathematics-maryam-mirzakhani/"
+ }
+ },
+ {
+ "id": "adele-goldberg",
+ "name": "Adele Goldberg",
+ "shortDescription": "Informática pionera, co-creadora del lenguaje de programación Smalltalk.",
+ "description": "Informática estadounidense reconocida por su influyente trabajo en la investigación y desarrollo de la programación orientada a objetos. Su contribución más destacada es la creación del lenguaje de programación Smalltalk, que revolucionó la forma en que se desarrollan software y sistemas informáticos. Goldberg ha sido una defensora incansable de la usabilidad y la accesibilidad en la informática, asegurando que las aplicaciones informáticas sean más amigables para los usuarios. Su trabajo ha allanado el camino para una programación más intuitiva y eficiente, y su legado perdura en la industria de la tecnología.",
+ "imageUrl": "https://laboratoria-dataverse-talks.netlify.app/public/adele-goldberg.jpg",
+ "facts": {
+ "yearOfBirth": "1945",
+ "birthPlace": "Chicago, Estados Unidos",
+ "mainField": "Ciencias de la Computación"
+ },
+ "extraInfo": {
+ "imageSource": "https://pionerasinformaticas.ujaen.es/en/computer-women-pioneers/adele-goldberg-en"
+ }
+ },
+ {
+ "id": "barbara-liskov",
+ "name": "Barbara Liskov",
+ "shortDescription": "Científica de la computación, pionera en programación orientada a objetos.",
+ "description": "Pionera de la informática, es conocida por su trabajo en la programación y la arquitectura de sistemas distribuidos. Fue la primera mujer en obtener un doctorado en ciencias de la computación en el Instituto de Tecnología de Stanford y desarrolló el lenguaje de programación CLU, que estableció fundamentos para la programación orientada a objetos. Liskov ha dejado una marca indeleble en la industria de la tecnología al liderar avances en sistemas de almacenamiento y seguridad informática. Su enfoque en la fiabilidad y la robustez ha influido en el diseño de sistemas críticos en todo el mundo.",
+ "imageUrl": "https://laboratoria-dataverse-talks.netlify.app/public/barbara-liskov.jpg",
+ "facts": {
+ "yearOfBirth": "1939",
+ "birthPlace": "Los Angeles, Estados Unidos",
+ "mainField": "Ciencia de la Computación"
+ },
+ "extraInfo": {
+ "imageSource": "https://medium.com/a-computer-of-ones-own/barbara-liskov-inventor-of-abstract-data-types-9f8908fdcf86"
+ }
+ },
+ {
+ "id": "grace-hopper",
+ "name": "Grace Hopper",
+ "shortDescription": "Científica de la computación, creadora del primer compilador.",
+ "description": "Una de las pioneras de la informática, es famosa por su contribución al desarrollo del primer compilador y al lenguaje de programación COBOL. Además, desafió los estereotipos de género en la década de 1940 al unirse a la Marina de los Estados Unidos y convertirse en la primera programadora de la computadora Harvard Mark I. Su legado abarca la creación de conceptos cruciales como la \"depuración de software\" y su influencia en el desarrollo de la informática moderna, estableciendo estándares para la programación de alto nivel y la innovación tecnológica.",
+ "imageUrl": "https://laboratoria-dataverse-talks.netlify.app/public/grace-hopper.jpg",
+ "facts": {
+ "yearOfBirth": "1906",
+ "yearOfDeath": "1992",
+ "birthPlace": "Nueva York, Estados Unidos",
+ "mainField": "Ciencia de la Computación"
+ },
+ "extraInfo": {
+ "imageSource": "https://photos.com/featured/grace-hopper-with-early-computer-bettmann.html"
+ }
+ },
+ {
+ "id": "margaret-hamilton",
+ "name": "Margaret Hamilton",
+ "shortDescription": "Científica de la computación, lideró desarrollo de software para Apollo 11.",
+ "description": "Destacada científica de la computación que desempeñó un papel fundamental en el programa Apollo de la NASA. Fue directora de la División de Ingeniería de Software en el Laboratorio de Instrumentación del MIT, donde desarrolló el concepto de \"ingeniería de software\". Su liderazgo en el desarrollo del software de vuelo para las misiones Apollo fue fundamental para el éxito de la llegada del hombre a la Luna. Hamilton es un ejemplo de cómo la tecnología y la ciencia pueden alcanzar logros extraordinarios cuando se combinan con un enfoque innovador y una visión audaz.",
+ "imageUrl": "https://laboratoria-dataverse-talks.netlify.app/public/margaret-hamilton.jpg",
+ "facts": {
+ "yearOfBirth": "1936",
+ "birthPlace": "Paoli, Indiana, Estados Unidos",
+ "mainField": "Ciencia de la Computación"
+ },
+ "extraInfo": {
+ "imageSource": "https://odetta.ai/blogs/margaret-hamilton-the-first-software-engineer"
+ }
+ },
+ {
+ "id": "radia-perlman",
+ "name": "Radia Perlman",
+ "shortDescription": "Ingeniera de redes, inventora del algoritmo Spanning Tree.",
+ "description": "Científica de la computación estadounidense, ha dejado una marca indeleble en el mundo de las redes informáticas y la seguridad. Conocida como \"la madre de Internet\", su invención del algoritmo Spanning Tree Protocol (STP) revolucionó la conectividad de redes y sentó las bases para la infraestructura de Internet moderna. A pesar de operar en un campo dominado por hombres, Perlman ha demostrado que las mujeres pueden liderar avances tecnológicos vitales. Su compromiso con la resiliencia de las redes y la ciberseguridad ha influido en la estabilidad y confiabilidad de la comunicación en línea.",
+ "imageUrl": "https://laboratoria-dataverse-talks.netlify.app/public/radia-perlman.jpg",
+ "facts": {
+ "yearOfBirth": "1951",
+ "birthPlace": "Portsmouth, Estados Unidos",
+ "mainField": "Ciencia de la Computación"
+ },
+ "extraInfo": {
+ "imageSource": "https://hackaday.com/2018/05/29/spanning-the-tree-dr-radia-perlman-untangling-networks/"
+ }
+ },
+ {
+ "id": "shafi-goldwasser",
+ "name": "Shafi Goldwasser",
+ "shortDescription": "Científica de la computación, ganadora del Premio Turing.",
+ "description": "Es una destacada criptógrafa y científica de la computación que ha contribuido significativamente a la teoría de la computación y la seguridad de la información. Su trabajo en criptografía de probabilidad y complejidad computacional ha sentado las bases para la seguridad en línea y la privacidad de datos en un mundo cada vez más digitalizado. Goldwasser es un ejemplo de cómo la ciencia de la computación puede abordar desafíos contemporáneos y proteger la información en la era de la información.",
+ "imageUrl": "https://laboratoria-dataverse-talks.netlify.app/public/shafi-goldwasser.jpg",
+ "facts": {
+ "yearOfBirth": "1958",
+ "birthPlace": "Nueva York, Estados Unidos",
+ "mainField": "Ciencia de la Computación"
+ },
+ "extraInfo": {
+ "imageSource": "https://www.jahresbericht2019.unibe.ch/successes_2019/einstein_lectures/index_eng.html"
+ }
+ },
+ {
+ "id": "chien-shiung-wu",
+ "name": "Chien-Shiung Wu",
+ "shortDescription": "Física experimental, contribuyó a la física nuclear.",
+ "description": "Física experimental de origen chino, desafiando los prejuicios de género y nacionalidad, realizó experimentos fundamentales en física de partículas y contribuyó significativamente a la comprensión de la desintegración beta. Su famoso experimento \"Wu\" refutó la paridad en la física de partículas, lo que cambió la forma en que los científicos comprenden las fuerzas fundamentales de la naturaleza. Wu demostró que el talento y la perseverancia pueden superar cualquier barrera, y su legado sigue siendo un faro de inspiración para científicas en todo el mundo.",
+ "imageUrl": "https://laboratoria-dataverse-talks.netlify.app/public/chien-shiung-wu.jpg",
+ "facts": {
+ "yearOfBirth": "1912",
+ "yearOfDeath": "1997",
+ "birthPlace": "Liuhe, Taicang, China",
+ "mainField": "Física"
+ },
+ "extraInfo": {
+ "imageSource": "https://www.thoughtco.com/chien-shiung-wu-biography-3530366"
+ }
+ },
+ {
+ "id": "hedy-lamarr",
+ "name": "Hedy Lamarr",
+ "shortDescription": "Actriz e inventora, contribuyó al desarrollo de la tecnología de espectro ensanchado.",
+ "description": "Actriz austroamericana y científica, fue una mente brillante en la intersección de la tecnología y el entretenimiento. Junto con el compositor George Antheil, inventó un sistema de comunicación secreta durante la Segunda Guerra Mundial que sentó las bases para la tecnología de espectro ensanchado y la comunicación inalámbrica moderna. Lamarr demostró que las mujeres pueden destacar tanto en el cine como en la ciencia, y su legado como inventora ha sido fundamental para el desarrollo de la tecnología de comunicación inalámbrica que usamos en la actualidad.",
+ "imageUrl": "https://laboratoria-dataverse-talks.netlify.app/public/hedy-lamarr.jpg",
+ "facts": {
+ "yearOfBirth": "1914",
+ "yearOfDeath": "2000",
+ "birthPlace": "Viena, Austria",
+ "mainField": "Actuación, Física"
+ },
+ "extraInfo": {
+ "imageSource": "https://www.sparkfun.com/news/6147"
+ }
+ },
+ {
+ "id": "maria-goeppert-mayer",
+ "name": "Maria Goeppert Mayer",
+ "shortDescription": "Física teórica, ganadora del Premio Nobel de Física.",
+ "description": "Física teórica alemana-estadounidense, dejó una profunda huella en la física nuclear y la mecánica cuántica. Fue la segunda mujer en recibir el Premio Nobel de Física y su trabajo pionero en la estructura de capas nucleares revolucionó nuestra comprensión de los núcleos atómicos. A pesar de las barreras de género en la academia, su dedicación y talento la llevaron a contribuir significativamente a la teoría nuclear, abriendo el camino para futuras investigadoras en un campo dominado por hombres. Maria Goeppert Mayer es un ejemplo inspirador de excelencia científica y perseverancia en la búsqueda del conocimiento.",
+ "imageUrl": "https://laboratoria-dataverse-talks.netlify.app/public/maria-goeppert-mayer.jpg",
+ "facts": {
+ "yearOfBirth": "1906",
+ "yearOfDeath": "1972",
+ "birthPlace": "Kattowitz, Alemania",
+ "mainField": "Física"
+ },
+ "extraInfo": {
+ "imageSource": "https://www.wsemexhibit.org/maria-goeppert-mayer.html"
+ }
+ },
+ {
+ "id": "lise-meitner",
+ "name": "Lise Meitner",
+ "shortDescription": "Física nuclear, contribuyó a la teoría de la fisión nuclear.",
+ "description": "Física austriaca-sueca, dejó un legado imborrable en la física nuclear y la radioquímica. A pesar de enfrentar obstáculos de género en una era dominada por hombres, colaboró con Otto Hahn en el descubrimiento de la fisión nuclear, un avance revolucionario. Aunque no compartió el Premio Nobel por este logro, su contribución fue crucial para comprender la energía nuclear y la física de partículas. Su determinación y pasión por la ciencia han inspirado a generaciones de científicas. Lise Meitner es un símbolo perdurable de la perseverancia femenina y el impacto duradero que las mujeres pueden tener en la ciencia.",
+ "imageUrl": "https://laboratoria-dataverse-talks.netlify.app/public/lise-meitner.jpg",
+ "facts": {
+ "yearOfBirth": "1878",
+ "yearOfDeath": "1968",
+ "birthPlace": "Viena, Austria",
+ "mainField": "Física"
+ },
+ "extraInfo": {
+ "imageSource": "https://www.bing.com/images/create/lise-meitner-in-nuclear-physics-lab-in-19502c-retro/651196e6bbcb4009950e9955c2199760"
+ }
+ },
+ {
+ "id": "sau-lan-wu",
+ "name": "Sau Lan Wu",
+ "shortDescription": "Física de partículas, contribuyó a experimentos clave en física de alta energía.",
+ "description": "Física experimental sinoamericana, ha sido una fuerza motriz en la física de partículas. Destacó en experimentos en el CERN y lideró investigaciones cruciales, como el descubrimiento del quark c, que expandieron nuestro entendimiento de la estructura fundamental de la materia. Wu superó desafíos en una disciplina dominada por hombres y ha sido un faro de inspiración para científicas de todo el mundo. Sau Lan Wu es un ejemplo de excelencia científica y de cómo las mujeres pueden dejar huella en la investigación de vanguardia.",
+ "imageUrl": "https://laboratoria-dataverse-talks.netlify.app/public/sau-lan-wu.jpg",
+ "facts": {
+ "yearOfBirth": "1940",
+ "birthPlace": "Hong Kong, China",
+ "mainField": "Física de Partículas"
+ },
+ "extraInfo": {
+ "imageSource": "https://repository.aip.org/islandora/object/nbla%3A315195"
+ }
+ },
+ {
+ "id": "jocelyn-bell-burnell",
+ "name": "Jocelyn Bell Burnell",
+ "shortDescription": "Astrónoma y astrofísica, co-descubridora de los púlsares.",
+ "description": "Astrofísica británica, es una figura extraordinaria en la ciencia. Durante su doctorado, hizo un descubrimiento revolucionario al detectar los primeros púlsares, estrellas de neutrones altamente magnetizadas que emiten pulsos regulares de radiación. A pesar de que su asesor recibió el Premio Nobel por este descubrimiento, Bell Burnell se ha convertido en un símbolo de perseverancia y contribución de las mujeres a la astronomía. Su carrera distinguida y su activismo en favor de la diversidad de género en la ciencia han inspirado a generaciones futuras de científicas. Jocelyn Bell Burnell es un ejemplo de valentía y excelencia científica.",
+ "imageUrl": "https://laboratoria-dataverse-talks.netlify.app/public/jocelyn-bell-burnell.jpg",
+ "facts": {
+ "yearOfBirth": "1943",
+ "birthPlace": "Belfast, Reino Unido",
+ "mainField": "Astronomía, Astrofísica"
+ },
+ "extraInfo": {
+ "imageSource": "https://womeninscienceweadmire.icfo.eu/wiswa-2/jocelyn-bell/"
+ }
+ },
+ {
+ "id": "dorothy-crowfoot-hodgkin",
+ "name": "Dorothy Crowfoot Hodgkin",
+ "shortDescription": "Química, ganadora del Premio Nobel de Química.",
+ "description": "Química británica, es una figura icónica en la ciencia que superó las barreras de género de su época. Su trabajo revolucionario en la cristalografía de rayos X permitió determinar la estructura de importantes biomoléculas, como la penicilina, la vitamina B12 y la insulina. Fue galardonada con el Premio Nobel de Química en 1964, siendo la tercera mujer en recibir este prestigioso reconocimiento. Hodgkin no solo fue una científica excepcional, sino también una defensora de la igualdad de género en la ciencia, allanando el camino para futuras generaciones de científicas y dejando un legado duradero en la investigación y la equidad.",
+ "imageUrl": "https://laboratoria-dataverse-talks.netlify.app/public/dorothy-crowfoot-hodgkin.jpg",
+ "facts": {
+ "yearOfBirth": "1910",
+ "yearOfDeath": "1994",
+ "birthPlace": "El Cairo, Egipto / Shipston-on-Stour, Reino Unido",
+ "mainField": "Química"
+ },
+ "extraInfo": {
+ "imageSource": "https://www.some.ox.ac.uk/news/jan-royall-why-we-must-champion-dorothy-hodgkin-for-the-50-note/"
+ }
+ },
+ {
+ "id": "marie-curie",
+ "name": "Marie Curie",
+ "shortDescription": "Física y química, ganadora de dos Premios Nobel.",
+ "description": "física y química de origen polaco y nacionalizada francesa, es una de las figuras más icónicas en la historia de la ciencia. Ganadora de dos premios Nobel (Física en 1903 y Química en 1911), Curie realizó investigaciones fundamentales sobre la radiactividad y el descubrimiento de los elementos radio y polonio. Su dedicación y valentía en la investigación científica, a pesar de los desafíos de la discriminación de género y los riesgos de la radiación, la convierten en un modelo a seguir para científicas de todo el mundo.",
+ "imageUrl": "https://laboratoria-dataverse-talks.netlify.app/public/marie-curie.jpg",
+ "facts": {
+ "yearOfBirth": "1867",
+ "yearOfDeath": "1934",
+ "birthPlace": "Varsovia, Polonia",
+ "mainField": "Física y Química"
+ },
+ "extraInfo": {
+ "imageSource": "https://www.historyextra.com/period/first-world-war/life-of-the-week-marie-curie/"
+ }
+ },
+ {
+ "id": "marjory-stephenson",
+ "name": "Marjory Stephenson",
+ "shortDescription": "Microbióloga y bioquímica, investigó en enzimología.",
+ "description": "Bioquímica británica, brilló como científica en una época en la que las mujeres enfrentaban obstáculos muy significativos en la investigación científica. Su trabajo pionero en microbiología, enzimología y bioquímica contribuyó al entendimiento de procesos fundamentales en la vida. Fue una de las primeras mujeres en ser elegida miembro de la Royal Society, un reconocimiento a su destacada contribución a la ciencia. Además, fue una mentora y defensora de las mujeres en la ciencia, inspirando y allanando el camino para futuras generaciones.",
+ "imageUrl": "https://laboratoria-dataverse-talks.netlify.app/public/marjory-stephenson.jpg",
+ "facts": {
+ "yearOfBirth": "1885",
+ "yearOfDeath": "1948",
+ "birthPlace": "Londres, Reino Unido",
+ "mainField": "Química, Microbiología, Bioquímica"
+ },
+ "extraInfo": {
+ "imageSource": "https://microbiologysociety.org/grants-prizes/all-prizes-and-competitions-/prize-lectures/marjory-stephenson-prize-lecture/marjory-stephenson-a-short-history.html"
+ }
+ },
+ {
+ "id": "rosalind-franklin",
+ "name": "Rosalind Franklin",
+ "shortDescription": "Química y cristalógrafa, contribuyó a la comprensión de la estructura del ADN.",
+ "description": "Química y cristalógrafa británica, destacó en un campo dominado por hombres y su contribución a la ciencia es innegable. Sus investigaciones pioneras en la cristalografía de rayos X fueron fundamentales para revelar la estructura del ADN en forma de doble hélice, un hito crucial en la biología molecular. A pesar de que su trabajo inicial fue subestimado, su determinación y habilidad científica allanaron el camino para futuros avances en genética y biología. Franklin es un ejemplo inspirador de cómo las mujeres pueden sobresalir en la ciencia, dejando un legado duradero en la investigación y la igualdad de género.",
+ "imageUrl": "https://laboratoria-dataverse-talks.netlify.app/public/rosalind-franklin.jpg",
+ "facts": {
+ "yearOfBirth": "1920",
+ "yearOfDeath": "1958",
+ "birthPlace": "Londres, Reino Unido",
+ "mainField": "Química, Cristalografía"
+ },
+ "extraInfo": {
+ "imageSource": ""
+ }
+ }
+];
diff --git a/src/dataFunctions.js b/src/dataFunctions.js
new file mode 100644
index 00000000..5de554af
--- /dev/null
+++ b/src/dataFunctions.js
@@ -0,0 +1,9 @@
+// Estas funciones son ejemplos, aquí puedes desarrollar tus propias funciones.
+
+export const example = () => {
+ return 'example';
+};
+
+export const anotherExample = () => {
+ return [];
+};
diff --git a/src/index.html b/src/index.html
new file mode 100644
index 00000000..05b0a0a6
--- /dev/null
+++ b/src/index.html
@@ -0,0 +1,11 @@
+
+
+
+
+ Dataverse
+
+
+
+
+
+
diff --git a/src/main.js b/src/main.js
new file mode 100644
index 00000000..cab4fb0c
--- /dev/null
+++ b/src/main.js
@@ -0,0 +1,6 @@
+import { example } from './dataFunctions.js';
+import { renderItems } from './view.js';
+
+import data from './data/dataset.js';
+
+console.log(example, renderItems(data), data);
diff --git a/src/style.css b/src/style.css
new file mode 100644
index 00000000..e69de29b
diff --git a/src/view.js b/src/view.js
new file mode 100644
index 00000000..0da7b7fa
--- /dev/null
+++ b/src/view.js
@@ -0,0 +1,6 @@
+export const renderItems = (data) => {
+ console.log(data)
+ // Aquí comienza tu código y puedes retornar lo que tu necesites
+ return 'example';
+};
+
diff --git a/test/.eslintrc b/test/.eslintrc
new file mode 100644
index 00000000..55f121d1
--- /dev/null
+++ b/test/.eslintrc
@@ -0,0 +1,5 @@
+{
+ "env": {
+ "jest": true
+ }
+}
diff --git a/test/data.js b/test/data.js
new file mode 100644
index 00000000..9b3c238b
--- /dev/null
+++ b/test/data.js
@@ -0,0 +1,5 @@
+// Aqui puedes agregar los datos que necesites para tus pruebas
+// al menos debe tener uno que se llame "data"
+
+export const data = [];
+
diff --git a/test/dataFunctions.spec.js b/test/dataFunctions.spec.js
new file mode 100644
index 00000000..72c5eb9d
--- /dev/null
+++ b/test/dataFunctions.spec.js
@@ -0,0 +1,18 @@
+import { example, anotherExample } from '../src/dataFunctions.js';
+import { data as fakeData } from './data.js';
+
+console.log(fakeData);
+
+describe('example', () => {
+
+ it('returns `example`', () => {
+ expect(example()).toBe('example');
+ });
+});
+
+describe('anotherExample', () => {
+
+ it('returns `anotherExample`', () => {
+ expect(anotherExample()).toBe('OMG');
+ });
+});
diff --git a/tests-read-only/.eslintrc b/tests-read-only/.eslintrc
new file mode 100644
index 00000000..766769ca
--- /dev/null
+++ b/tests-read-only/.eslintrc
@@ -0,0 +1,6 @@
+{
+ "env": {
+ "jest": true,
+ "es6": true
+ }
+}
diff --git a/tests-read-only/e2e/app.spec.js b/tests-read-only/e2e/app.spec.js
new file mode 100644
index 00000000..ee55be07
--- /dev/null
+++ b/tests-read-only/e2e/app.spec.js
@@ -0,0 +1,155 @@
+// @ts-check
+import { test, expect } from '@playwright/test';
+
+test.describe('Pagina interraciones', () => {
+
+ const sortOptions = { asc: 'asc', desc: 'desc'};
+ const liSelector = '#root > ul > li';
+
+ const getItempropValues = async (page, property) => {
+ const liElements = await page.$$(liSelector);
+ const values = await Promise.all(liElements.map(async li => {
+ const value = await li.$eval(`[itemprop=${property}]`, (el) => el.textContent);
+ return value;
+ }));
+ return values;
+ };
+
+ const getDataIds = (elements) => Promise.all(elements.map(async (el) => await el.getAttribute('data-id')));
+
+ const getSortOptions = async (page) => {
+ const selectSortEl = await page.getByTestId('select-sort')
+ let sortByProperty = await selectSortEl.getAttribute('name');
+ const sortOrderEl = await page.$('[name="sort-order"]');
+
+ if (!sortOrderEl) { // simple sort UI with just one type of sort
+ const sortOrder = await selectSortEl.getAttribute('value');
+ return {
+ selectSortEl,
+ sortByProperty,
+ sortOrderEl: selectSortEl,
+ sortOrder
+ };
+ }
+
+ const sortOrder = await sortOrderEl.getAttribute('value');
+ sortByProperty = await selectSortEl.getAttribute('name');
+ return {
+ selectSortEl,
+ sortByProperty,
+ sortOrderEl,
+ sortOrder
+ }
+ }
+
+ const selectSortOrder = async (sortOrderEl, orderOption) => {
+ try {
+ await sortOrderEl.selectOption(orderOption);
+ } catch(errSelect) {
+ try {
+ await sortOrderEl.check(orderOption);
+ } catch(errCheck) {
+ throw new Error(`No se puede seleccionar el orden ${orderOption} en el elemento ${sortOrderEl},
+ error select: ${errSelect}, error check: ${errCheck}`);
+ }
+ }
+ };
+
+ test.describe('sort', () => {
+
+ let sortOrderEl, sortByProperty;
+
+ test.beforeEach(async ({ page }) => {
+ await page.goto('http://localhost:3000/');
+ ({ sortOrderEl, sortByProperty } = await getSortOptions(page));
+ });
+
+ test('de ascendente "asc" a descendente "desc"', async ({ page }) => {
+ // await sortOrderEl.selectOption(sortOptions.asc);
+ await selectSortOrder(sortOrderEl,sortOptions.asc);
+ const sortedValuesAsc = await getItempropValues(page, sortByProperty);
+
+ await selectSortOrder(sortOrderEl,sortOptions.desc);
+ //await sortOrderEl.selectOption(sortOptions.desc);
+ const sortedValuesDesc = await getItempropValues(page, sortByProperty);
+
+ expect(sortedValuesDesc).toEqual(sortedValuesAsc.reverse());
+ });
+
+ test('de descendente "desc" a ascendente "asc"', async ({ page }) => {
+ await selectSortOrder(sortOrderEl,sortOptions.desc);
+ const sortedValuesDesc = await getItempropValues(page, sortByProperty);
+
+ // await sortOrderEl.selectOption(sortOptions.asc);
+ await selectSortOrder(sortOrderEl,sortOptions.asc);
+ const sortedValuesAsc = await getItempropValues(page, sortByProperty);
+
+ expect(sortedValuesAsc).toEqual(sortedValuesDesc.reverse());
+ });
+ });
+
+ test.describe('filter', () => {
+
+ let selectFilter;
+
+ test.beforeEach(async ({ page }) => {
+ await page.goto('http://localhost:3000/');
+ selectFilter = await page.getByTestId('select-filter');
+ });
+
+ test(`cuando elige un filter a otro los resultados se cambian`, async ({ page }) => {
+ await selectFilter.selectOption({ index: 1 });
+ const originalLis = await page.$$(liSelector);
+ const originalIds = await getDataIds(originalLis);
+
+ await selectFilter.selectOption({ index: 2 });
+ const currentLis = await page.$$(liSelector);
+ const currentIds = await getDataIds(currentLis)
+ expect(originalIds).not.toEqual(currentIds);
+ });
+ });
+
+ test.describe('sort + filter', () => {
+
+ let sortOrderEl, sortByProperty, selectFilter;
+
+ test.beforeEach(async ({ page }) => {
+ await page.goto('http://localhost:3000/');
+
+ selectFilter = await page.getByTestId('select-filter');
+ ({ sortOrderEl, sortByProperty } = await getSortOptions(page));
+ });
+
+ test('cuando elige un filtro y un sort, los resultados son afectado de ambos', async ({ page }) => {
+ await selectFilter.selectOption({ index: 1 });
+
+ // sacamos los valores de propiedad en el orden que ocurre en la pagina
+ // sin sort
+ const valuesNoSorted = await getItempropValues(page, sortByProperty);
+
+ await selectSortOrder(sortOrderEl,sortOptions.asc);
+ const valuesSortedAsc = await getItempropValues(page, sortByProperty);
+
+ // await sortOrderEl.selectOption(sortOptions.desc);
+ await selectSortOrder(sortOrderEl,sortOptions.desc);
+ const valuesSortedDesc = await getItempropValues(page, sortByProperty);
+
+ expect(valuesSortedAsc).toEqual([...valuesNoSorted].sort());
+ expect(valuesSortedDesc).toEqual([...valuesNoSorted].sort().reverse());
+ });
+
+ test('el button "button-clear" limpia los filtros y sort, volvemos a los resultados original', async ({ page }) => {
+ await page.goto('http://localhost:3000/');
+ const originalLis = await page.$$(liSelector);
+ const select = await page.getByTestId('select-filter');
+ await select.selectOption({ index: 1 });
+ const liElements = await page.$$(liSelector);
+ expect(liElements.length).not.toEqual(originalLis.length); // probar si no hay mismo cantidad elementos
+
+ const buttonReset = await page.getByTestId('button-clear');
+ await buttonReset.click();
+ const clearLiElements = await page.$$(liSelector);
+ expect(clearLiElements.length).toEqual(originalLis.length);
+ });
+ });
+});
diff --git a/tests-read-only/oa/oa-css.spec.js b/tests-read-only/oa/oa-css.spec.js
new file mode 100644
index 00000000..f8e14782
--- /dev/null
+++ b/tests-read-only/oa/oa-css.spec.js
@@ -0,0 +1,202 @@
+/* eslint-disable indent */
+/**
+ * @jest-environment jsdom
+*/
+import fs from 'fs';
+import css from 'css';
+import { renderItems } from '../../src/view.js';
+import { data as fakeData } from '../../test/data.js';
+
+const html = fs.readFileSync('./src/index.html', 'utf-8');
+document.body.innerHTML = html;
+
+const stylesPath = document.querySelector('link[rel="stylesheet"]').getAttribute('href');
+const style = fs.readFileSync('./src/' + stylesPath, 'utf-8');
+const { rules } = css.parse(style).stylesheet;
+
+const BOX_MODEL_ATTRIBUTES = ['width', 'height', 'margin', 'padding', 'border', 'box-sizing'];
+const FLEXBOX_DECLARATION = ['display', 'flex'];
+const FLEXBOX_ATTRIBUTES = ['flex-wrap', 'flex-direction', 'justify-content', 'align-items'];
+
+const renderDOM = (data) => {
+ const items = renderItems(data);
+ // function renderItems can return html string or an node element
+ if (typeof items === 'string') {
+ document.querySelector('#root').innerHTML = items;
+ } else if (items instanceof HTMLElement) {
+ document.querySelector('#root').appendChild(items);
+ } else {
+ throw new Error('Error: renderItems should return an HTML string or an HTMLElement');
+ }
+}
+
+const getRulesForSelector = (selector) => {
+ return rules.filter(
+ (rule) =>
+ rule.type === 'rule' &&
+ rule.selectors.some((s) => s.trim().includes(selector))
+ );
+}
+
+// returns an array of css declaration objects in format
+// [{ 'display": 'block' }, { 'justify-content': 'center' }];
+const getCSSDeclarationsForRules = (rules) => {
+ return rules.reduce((total, rule) => {
+ const declarations = rule.declarations.map(({ property, value }) => ({ [property]: value }));
+ return [...total, ...declarations];
+ }, []);
+}
+
+// get css for an el's classes
+// returns in format [{ 'display': 'block' }, { 'justify-content': 'center'}];
+const getDeclarationsForElClasses = (el) => {
+ const elClasses = Array.from(el.classList.values());
+ return elClasses.reduce((allDeclarations, className) => {
+ const rules = getRulesForSelector(`.${className}`);
+ // there can be more than one rule for a class
+ const declarationsForClass = getCSSDeclarationsForRules(rules);
+ return [...allDeclarations, ...declarationsForClass];
+ }, []);
+}
+
+describe('CSS', () => {
+
+ describe('Uso de selectores de CSS', () => {
+
+ beforeEach(() => {
+ renderDOM(fakeData);
+ });
+
+ it('elementos tienen un class con CSS', () => {
+ const elementsLi = document.querySelectorAll('#root > ul > li');
+ // all lis should have same classes since rendered dinamically
+ // so not checking for common classes here
+ elementsLi.forEach((li) => {
+ const liRulesAttributes = getDeclarationsForElClasses(li);
+ expect(liRulesAttributes.length).toBeGreaterThan(0);
+ });
+ expect.hasAssertions();
+ });
+
+ it('Se usan selectores CSS de tipo para ', () => {
+ const headerRules = getRulesForSelector('header');
+ expect(headerRules.length).toBeGreaterThan(0);
+ });
+
+ it('Se usan selectores CSS de tipo para ', () => {
+ const footerRules = getRulesForSelector('footer');
+ expect(footerRules.length).toBeGreaterThan(0);
+ });
+
+ it('Se usan selectores CSS de tipo para ', () => {
+ const selectRules = getRulesForSelector('select');
+ expect(selectRules.length).toBeGreaterThan(0);
+ });
+ });
+
+ describe('Uso de flexbox', () => {
+
+ beforeEach(() => {
+ renderDOM(fakeData);
+ });
+
+ it('Uso de flexbox en el elemento de ', () => {
+ const ul = document.querySelector('#root > ul');
+ const cssForTag = getCSSDeclarationsForRules(getRulesForSelector(ul.tagName.toLowerCase()));
+ const cssForId = getCSSDeclarationsForRules(getRulesForSelector(`#${ul.id}`));
+ const cssForClasses = getDeclarationsForElClasses(ul);
+ const cssDeclarations = [...cssForClasses, ...cssForId, ...cssForTag];
+
+ // expect to have display: flex
+ expect(
+ cssDeclarations.some((declaration) => {
+ const [ flexboxProperty, flexboxValue ] = FLEXBOX_DECLARATION;
+ const [ property, value ] = Object.entries(declaration)[0];
+ return property === flexboxProperty && value === flexboxValue;
+ })
+ ).toBe(true);
+ // and at least one other flexbox property
+ expect(
+ cssDeclarations.some((declaration) => {
+ const [ property ] = Object.entries(declaration)[0];
+ return FLEXBOX_ATTRIBUTES.includes(property);
+ })
+ ).toBe(true);
+ });
+
+ it('Los elementos de filtro y sort estan en un elemento que usa flexbox', () => {
+ const selects = document.querySelectorAll('body select');
+ expect(selects.length).toBeGreaterThan(1);
+
+ // if not more than one select, don't check for common parent
+ if (selects.length > 1) {
+
+ const parents = (node) => {
+ const nodes = [];
+ while ((node = node.parentNode)) {
+ nodes.push(node)
+ }
+ return nodes
+ }
+
+ const select1Parents = parents(selects[0]);
+ const select2Parents = parents(selects[1]);
+ const commonParent = select1Parents.find((node) => select2Parents.includes(node));
+
+ const cssForTag = getCSSDeclarationsForRules(getRulesForSelector(commonParent.tagName.toLowerCase()));
+ const cssForId = getCSSDeclarationsForRules(getRulesForSelector(`#${commonParent.id}`));
+
+ const cssForClasses = getDeclarationsForElClasses(commonParent);
+ const allCSSDeclarations = [...cssForId, ...cssForTag, ...cssForClasses];
+ expect(allCSSDeclarations.some((declaration) => {
+ const [ displayProperty, flexboxValue ] = FLEXBOX_DECLARATION;
+ const [ property, value ] = Object.entries(declaration);
+ return property === displayProperty && value === flexboxValue;
+ }));
+ }
+ });
+ });
+
+ describe('Modelo de caja (box model)', () => {
+ it('Se usan atributos de modelo de caja en clase CSS para ', () => {
+ const elementsLi = document.querySelectorAll('#root > ul > li');
+ elementsLi.forEach((li) => {
+ expect(
+ getDeclarationsForElClasses(li).some((declaration) => {
+ const [ property ] = Object.entries(declaration)[0];
+ return BOX_MODEL_ATTRIBUTES.some(attr => property.startsWith(attr));
+ })
+ ).toBe(true);
+ });
+ });
+
+ it('Se usan atributos de modelo de caja para o ', () => {
+ const HEADER_SELECTOR = 'header';
+ const FOOTER_SELECTOR = 'footer'
+
+ const elements = [
+ {
+ element: document.querySelector(HEADER_SELECTOR),
+ selector: HEADER_SELECTOR,
+ },
+ {
+ element: document.querySelector(FOOTER_SELECTOR),
+ selector: FOOTER_SELECTOR
+ }
+ ];
+
+ const isUsingBoxModelSomeElement = elements.some(elementObj => {
+ if (!elementObj.element) return;
+ const rules = getRulesForSelector(elementObj.selector);
+ const declarations = getCSSDeclarationsForRules(rules);
+
+ return declarations.some((declaration) => {
+ const [property] = Object.entries(declaration)[0];
+ return BOX_MODEL_ATTRIBUTES.some(attr => property.startsWith(attr));
+ })
+ });
+
+ expect(isUsingBoxModelSomeElement).toBe(true)
+ })
+ });
+});
diff --git a/tests-read-only/oa/oa-html.spec.js b/tests-read-only/oa/oa-html.spec.js
new file mode 100644
index 00000000..8727ff74
--- /dev/null
+++ b/tests-read-only/oa/oa-html.spec.js
@@ -0,0 +1,123 @@
+/**
+ * @jest-environment jsdom
+*/
+import fs from 'fs';
+import { renderItems } from '../../src/view.js';
+import { data as fakeData } from '../../test/data.js';
+
+const html = fs.readFileSync('./src/index.html', 'utf-8');
+document.body.innerHTML = html;
+
+const renderDOM = (data) => {
+ const items = renderItems(data);
+ // function renderItems can return html string or an node element
+ if (typeof items === 'string') {
+ document.querySelector('#root').innerHTML = items;
+ } else if (items instanceof HTMLElement) {
+ document.querySelector('#root').appendChild(items);
+ } else {
+ throw new Error('Error: renderItems should return an HTML string or an HTMLElement');
+ }
+}
+
+describe('Uso de HTML semántico', () => {
+
+ beforeEach(() => {
+ renderDOM(fakeData);
+ });
+
+ describe('', () => {
+ let header, h1;
+ beforeEach(() => {
+ header = document.querySelector('header');
+ h1 = header.querySelector('h1');
+ });
+
+ it('La aplicación usa un ', () => {
+ expect(header).not.toBeNull();
+ });
+
+ it(' no tiene atributo "id"', () => {
+ expect(header.getAttribute('id')).toBeNull();
+ });
+
+ it(' no tiene atributo "class"', () => {
+ expect(header.getAttribute('class')).toBeNull();
+ });
+
+ it(' es padre de un ', () => {
+ expect(h1).not.toBeNull();
+ });
+
+ it(' no tiene atributo "id"', () => {
+ expect(h1.getAttribute('id')).toBeNull();
+ });
+
+ it(' no tiene atributo "class"', () => {
+ expect(h1.getAttribute('class')).toBeNull();
+ });
+ });
+
+ describe('', () => {
+ let select = [];
+ beforeEach(()=>{
+ select = document.querySelectorAll('select');
+ });
+
+ it('La aplicación usa un ', () => {
+ expect(select.length).toBeGreaterThan(0);
+ });
+
+ it(' tiene atributo "name"', () => {
+ Array.from(select).forEach((element) => {
+ expect(element.getAttribute('name').length).toBeGreaterThan(0);
+ })
+ expect.hasAssertions();
+ });
+
+ it(' no tiene atributo "class"', () => {
+ Array.from(select).forEach((element) => {
+ expect(element.getAttribute('class')).toBeNull();
+ })
+ expect.hasAssertions();
+ });
+
+ it(' existe y usan el atributo "for" y el valor de "for" coincide con el "id" de control', () => {
+ const label = document.querySelectorAll('label');
+ Array.from(select).forEach((element) => {
+ const previousFor = element.previousElementSibling.getAttribute('for');
+ expect(previousFor).toBe(element.id);
+ })
+ expect(select.length && label.length).toBeGreaterThan(0);
+ })
+
+ });
+
+ describe('', () => {
+ let lis = [];
+ beforeEach(()=>{
+ lis = document.querySelectorAll('#root > ul > li');
+ });
+
+ it('La aplicacion usa un con para los elementos de data', () => {
+ expect(lis.length).toBeGreaterThan(0);
+ });
+
+ it(' tiene atributo "itemtype"', () => {
+ Array.from(lis).forEach((li) => {
+ expect(li.getAttribute('itemtype')).not.toBeNull();
+ expect(li.getAttribute('itemscope')).not.toBeNull();
+ })
+ expect.hasAssertions();
+ });
+
+ });
+
+ describe('', () => {
+ it('La aplicación usa un ', () => {
+ const footer = document.querySelector('footer');
+ expect(footer).not.toBeNull();
+ });
+ });
+
+});
diff --git a/tests-read-only/oa/oa-javascript.spec.js b/tests-read-only/oa/oa-javascript.spec.js
new file mode 100644
index 00000000..7370d696
--- /dev/null
+++ b/tests-read-only/oa/oa-javascript.spec.js
@@ -0,0 +1,149 @@
+import acorn from 'acorn'
+import fs from 'fs';
+
+//read analyzer.js file
+const mainCode = fs.readFileSync("src/dataFunctions.js", "utf8");
+const renderCode = fs.readFileSync("src/view.js", "utf8");
+const mainAst = acorn.parse(mainCode, { ecmaVersion: 2020, sourceType: "module" });
+const ast = acorn.parse(renderCode, { ecmaVersion: 2020, sourceType: "module", program: mainAst});
+
+const getASTMetrics = (node, [
+ sortCalls,
+ filterCalls,
+ reduceCalls,
+ mapCalls,
+ constStatements,
+ forStatements,
+ forEachCalls,
+ ifelseStatements,
+ exportStatements,
+]) => {
+
+ if (node.type === "CallExpression" &&
+ node.callee.type === "MemberExpression" &&
+ node.callee.property.type === "Identifier" &&
+ (node.callee.property.name === "sort" || node.callee.property.name === "toSorted")) {
+ sortCalls.push(node);
+ }
+
+ if (node.type === "CallExpression" &&
+ node.callee.type === "MemberExpression" &&
+ node.callee.property.type === "Identifier" &&
+ node.callee.property.name === "filter") {
+ filterCalls.push(node);
+ }
+
+ if (node.type === "CallExpression" &&
+ node.callee.type === "MemberExpression" &&
+ node.callee.property.type === "Identifier" &&
+ node.callee.property.name === "reduce") {
+ reduceCalls.push(node);
+ }
+
+ if (node.type === "CallExpression" &&
+ node.callee.type === "MemberExpression" &&
+ node.callee.property.type === "Identifier" &&
+ node.callee.property.name === "map") {
+ mapCalls.push(node);
+ }
+
+ if (node.type === "VariableDeclaration" && node.kind === "const") {
+ constStatements.push(node);
+ }
+
+ if (node.type === "ForStatement") {
+ forStatements.push(node);
+ }
+
+ if (node.type === "CallExpression" &&
+ node.callee.type === "MemberExpression" &&
+ node.callee.property.type === "Identifier" &&
+ node.callee.property.name === "forEach") {
+ forEachCalls.push(node);
+ }
+
+ if (node.type === "IfStatement") {
+ ifelseStatements.push(node);
+ }
+
+ if ([
+ "ExportDeclaration",
+ "ExportAllDeclaration",
+ "ExportNamedDeclaration",
+ "ExportDefaultDeclaration",
+ "ExportSpecifier",
+ "ExportDefaultSpecifier",
+ ].includes(node.type)) {
+ exportStatements.push(node);
+ }
+
+ for (const key in node) {
+ /* eslint-disable-next-line no-prototype-builtins */
+ if (node.hasOwnProperty(key)) {
+ const child = node[key];
+ if (typeof child === "object" && child !== null) {
+ getASTMetrics(child, [
+ sortCalls,
+ filterCalls,
+ reduceCalls,
+ mapCalls,
+ constStatements,
+ forStatements,
+ forEachCalls,
+ ifelseStatements,
+ exportStatements,
+ ]);
+ }
+ }
+ }
+}
+
+const metrics = [[], [], [], [], [], [], [], [], [], [], [], []];
+getASTMetrics(ast, metrics);
+const [
+ sortCalls,
+ filterCalls,
+ reduceCalls,
+ mapCalls,
+ constStatements,
+ forStatements,
+ forEachCalls,
+ ifelseStatements,
+ exportStatements,
+] = metrics;
+
+describe('Arrays', () => {
+ it('Se usan métodos para manipular arrays como "sort"', () => {
+ expect(sortCalls.length).toBeGreaterThan(0);
+ });
+ it('Se usan métodos para manipular arrays como "filter"', () => {
+ expect(filterCalls.length).toBeGreaterThan(0);
+ });
+ it('Se usan métodos para manipular arrays como "reduce"', () => {
+ expect(reduceCalls.length).toBeGreaterThan(0);
+ });
+ it('Se prefiere el uso de forEach sobre for', () => {
+ expect(forStatements.length <= forEachCalls.length).toBe(true);
+ });
+ it('Se usan métodos para manipular arrays como "map"', () => {
+ expect(mapCalls.length).toBeGreaterThan(0);
+ });
+});
+
+describe('Variables', () => {
+ it('Se declaran variables con "const"', () => {
+ expect(constStatements.length).toBeGreaterThan(0);
+ });
+});
+
+describe('Uso de condicionales', () => {
+ it('Se usa el statement "if...else"', () => {
+ expect(ifelseStatements.length).toBeGreaterThan(0);
+ });
+});
+
+describe('Módulos de ECMAScript', () => {
+ it('Se usa "export"', () => {
+ expect(exportStatements.length).toBeGreaterThan(0);
+ });
+});
diff --git a/tests-read-only/oa/oa-prompting.spec.js b/tests-read-only/oa/oa-prompting.spec.js
new file mode 100644
index 00000000..c3c801ed
--- /dev/null
+++ b/tests-read-only/oa/oa-prompting.spec.js
@@ -0,0 +1,130 @@
+/* eslint-disable no-console */
+import data from '../../src/data/dataset.js'
+
+describe('Test de data set', () => {
+ describe('Dataset', () => {
+ it('Debe ser un arreglo', () => {
+ expect(Array.isArray(data)).toBe(true)
+ });
+ it('Debe tener una longitud de 24 objetos', () => {
+ expect(data.length).toBe(24)
+ });
+ it('Los ids son todos distintos', () => {
+ const ids = data.map(item => item.id);
+ const idsSet = new Set(ids);
+ expect(ids.length).toBe(idsSet.size);
+ });
+ it('Los elementos comparten en sus "facts" al menos 3 propiedades', () => {
+ // Iteramos todos los elementos, obtenemos los keys de la propiedad `facts`
+ // y vamos acumulando en un objeto la cantidad de veces que aparece cada key
+ const factsKeysCount = data.reduce((acc, item) => {
+ Object.keys(item.facts).forEach((key) => {
+ acc[key] = acc[key] ? acc[key] + 1 : 1;
+ });
+ return acc;
+ }, {});
+
+ // validamos que al menos 3 de las propiedades de `facts` aparezcan
+ // en todos los elementos
+ const minCount = 3;
+ const validKeysCount = Object.values(factsKeysCount)
+ .filter((count) => count === data.length).length;
+ expect(validKeysCount).toBeGreaterThanOrEqual(minCount);
+ });
+ });
+
+ data.forEach((item) => {
+ describe(`*** ${item.name || item.id} ***`, () => {
+ it('Debe tener las propiedades requeridas', () => {
+ expect(item).toHaveProperty('id');
+ expect(item).toHaveProperty('name');
+ expect(item).toHaveProperty('shortDescription');
+ expect(item).toHaveProperty('description');
+ expect(item).toHaveProperty('imageUrl');
+ expect(item).toHaveProperty('facts');
+ });
+
+ describe("id", () => {
+ it('Debe ser un string', () => {
+ expect(typeof item.id).toBe('string');
+ });
+
+ it('Tiene el formato correcto', () => {
+ expect(/^[a-zA-Z0-9_\-]*$/.test(item.id)).toBe(true);
+ expect(item.id.length).toBeGreaterThan(0);
+ expect(item.id.length).toBeLessThanOrEqual(32);
+ });
+ });
+
+ describe("name", () => {
+ it('Debe ser un string', () => {
+ expect(typeof item.name).toBe('string');
+ });
+ });
+
+ describe('shortDescripcion', () => {
+ it('Debe ser un string', () => {
+ expect(typeof item.description).toBe('string');
+ });
+
+ it('Debe tener maximo 20 palabras', () => {
+ const words = item.shortDescription.split(/\s+/).filter(word => word !== '');
+ expect(words.length).toBeLessThanOrEqual(20);
+ });
+ });
+
+
+ describe('descripcion', () => {
+ it('Debe ser un string', () => {
+ expect(typeof item.description).toBe('string');
+ });
+
+ it('Debe tener entre 80 y 100 palabras', () => {
+ const words = item.description.split(/\s+/).filter(word => word !== '');
+ expect(words.length).toBeGreaterThanOrEqual(80);
+ expect(words.length).toBeLessThanOrEqual(100);
+ });
+ });
+
+ describe("imageUrl", () => {
+ it('Debe ser un string', () => {
+ expect(typeof item.imageUrl).toBe('string');
+ });
+
+ it('Debe ser una URL válida', () => {
+ const pathRegex = /\.(jpg|jpeg|png|gif|bmp|svg|webp)$/;
+ const urlRegex = /^(https?:\/\/)?([\da-z.-]+)\.([a-z.]{2,6})([/\w.-]*)*\/?$/;
+ expect(pathRegex.test(item.imageUrl) || urlRegex.test(item.imageUrl)).toBe(true);
+ });
+ });
+
+ describe('facts', () => {
+ it('Debe ser un objeto', () => {
+ expect(typeof item.facts).toBe('object');
+ })
+
+ it('Debe tener al menos 3 propiedades', () => {
+ expect(Object.keys(item.facts).length).toBeGreaterThanOrEqual(3);
+ });
+
+ it('Los nombres de todas las propiedades deben tener formato camelCase', () => {
+ expect.hasAssertions();
+ Object.keys(item.facts).forEach((k) => {
+ expect(/^[a-z]+([A-Z][a-z]*)*$/.test(k)).toBe(true);
+ });
+ });
+
+ it('Los valores de todas las propiedades deben tener el formato esperado', () => {
+ expect.hasAssertions();
+ Object.values(item.facts).forEach((v) => {
+ expect(['number', 'boolean', 'string']).toContain(typeof v);
+ if (typeof v === 'string') {
+ expect(v.length).toBeGreaterThan(0);
+ expect(v.length).toBeLessThanOrEqual(64);
+ }
+ });
+ });
+ });
+ });
+ });
+});
diff --git a/tests-read-only/oa/oa-web-api.spec.js b/tests-read-only/oa/oa-web-api.spec.js
new file mode 100644
index 00000000..8d8aa358
--- /dev/null
+++ b/tests-read-only/oa/oa-web-api.spec.js
@@ -0,0 +1,164 @@
+import acorn from 'acorn'
+import fs from 'fs';
+
+//read analyzer.js file
+const mainCode = fs.readFileSync("src/main.js", "utf8");
+const renderCode = fs.readFileSync("src/view.js", "utf8");
+const mainAst = acorn.parse(mainCode, { ecmaVersion: 2020, sourceType: "module" });
+const ast = acorn.parse(renderCode, { ecmaVersion: 2020, sourceType: "module", program: mainAst});
+
+
+const getASTMetrics = (node, [
+ querySelectorCalls=[],
+ getElementByIdCalls=[],
+ addEventListenerCalls=[],
+ importStatements=[],
+ textContents=[],
+ innerHTMLs=[],
+ createElementCalls=[],
+ templateCalls=[] ]) => {
+
+ if (
+ node.type === "CallExpression" &&
+ node.callee.type === "MemberExpression" &&
+ node.callee.property.name === "querySelector"
+ ) {
+ querySelectorCalls.push(node);
+ }
+
+ if (
+ node.type === "CallExpression" &&
+ node.callee.type === "MemberExpression" &&
+ node.callee.property.name === "getElementById"
+ ) {
+ getElementByIdCalls.push(node);
+ }
+
+ if (node.type === "CallExpression" &&
+ node.callee.type === "MemberExpression" &&
+ node.callee.property.type === "Identifier" &&
+ node.callee.property.name === "addEventListener") {
+ addEventListenerCalls.push(node);
+ }
+
+ if (node.type === "ImportDeclaration") {
+ importStatements.push(node);
+ }
+
+ if (node.type === "AssignmentExpression" &&
+ node.left.type === "MemberExpression" &&
+ node.left.property.type === "Identifier" &&
+ node.left.property.name === "textContent") {
+ textContents.push(node);
+ }
+
+ if (node.type === "AssignmentExpression" &&
+ node.left.type === "MemberExpression" &&
+ node.left.property.type === "Identifier" &&
+ node.left.property.name === "innerHTML") {
+ innerHTMLs.push(node);
+ }
+
+ if (node.type === "CallExpression" &&
+ node.callee.type === "MemberExpression" &&
+ node.callee.property.type === "Identifier" &&
+ (node.callee.property.name === "createElement" || node.callee.property.name === "appendChild")) {
+ createElementCalls.push(node);
+ }
+
+ if (node.type === "VariableDeclaration" &&
+ node.declarations &&
+ node.declarations[0].init &&
+ node.declarations[0].init['type'] === "TemplateLiteral"){
+ templateCalls.push(node);
+ }
+
+ for (const key in node) {
+ /* eslint-disable-next-line no-prototype-builtins */
+ if (node.hasOwnProperty(key)) {
+ const child = node[key];
+ if (typeof child === "object" && child !== null) {
+ getASTMetrics(child, metrics);
+ }
+ }
+ }
+}
+
+const metrics = [[], [], [], [], [], [], [], []];
+getASTMetrics(ast, metrics);
+
+const [
+ querySelectorCalls,
+ getElementByIdCalls,
+ addEventListenerCalls,
+ importStatements,
+ textContents,
+ innerHTMLs,
+ createElementCalls,
+ templateCalls
+] = metrics;
+
+describe('Uso de selectores del DOM', () => {
+
+ it('Se usa el selector del DOM querySelector', () => {
+ expect(querySelectorCalls.length).toBeGreaterThan(0);
+ });
+
+ it('Se prefiere el uso de querySelector sobre getElementById', () => {
+ expect(getElementByIdCalls.length < querySelectorCalls.length).toBe(true);
+ });
+
+});
+
+describe('Manejo de eventos del DOM', () => {
+
+ it('Se registra un Event Listener para el evento "change"', () => {
+ expect(
+ addEventListenerCalls.some((node) => node.arguments[0].value === "change")
+ ).toBeTruthy();
+ });
+
+ it('Se registra un Event Listener para el evento "click"', () => {
+ expect(
+ addEventListenerCalls.some((node) => node.arguments[0].value === "click")
+ ).toBeTruthy();
+ });
+
+ it('Se registra un Event Listener con un parametro de evento', () => {
+ expect(
+ addEventListenerCalls.some((node) => {
+ // si el segundo argumento es una referencia a una función por su nombre
+ if (node.arguments[1].type === 'Identifier') return true;
+ // si la función tiene parámetros definidos
+ if (!node.arguments[1].params || node.arguments[1].params.length === 0) return false;
+ // que existe un param tipo { target } { currentTarget }
+ const hasTargetParam = node.arguments[1].params[0].type === 'ObjectPattern' && (
+ node.arguments[1].params[0].properties[0].key.name === ('target') ||
+ node.arguments[1].params[0].properties[0].key.name === ('currentTarget'));
+ // que existe un param tipo e, event
+ const hasNamedEventParam = !!(node.arguments[1].params[0].name);
+ return hasNamedEventParam || hasTargetParam;
+ })
+ ).toBeTruthy();
+ });
+});
+
+describe('Manipulación dinámica del DOM', () => {
+
+ it('Se actualiza el DOM al modificar el atributo "innerHTML" o "textContent"', () => {
+ expect(textContents.length + innerHTMLs.length).toBeGreaterThan(0);
+ });
+
+ it('Existe manipulación dinamica mediante createElement o template strings', () => {
+ expect(createElementCalls.length || templateCalls.length).toBeGreaterThan(0);
+ });
+
+});
+
+describe('Módulos de ECMAScript', () => {
+
+ it('Se usa "import"', () => {
+ expect(importStatements.length).toBeGreaterThan(0);
+ });
+
+});
diff --git a/thumb.png b/thumb.png
new file mode 100644
index 00000000..5b40e35d
Binary files /dev/null and b/thumb.png differ