The implementation of innerText depends on whether or not the element is visible. As @Nisala noted in the comments, if you set the display attribute of the div back to block, the innerText contains your newline characters again.
const input = document.querySelector("#input");
function performExperiment() {
  console.log(`innerText before style change: ${input.innerText}`);
  
  input.style.display = "none";
  console.log(`innerText after first style change: ${input.innerText}`);
  
  input.style.display = "block";
  console.log(`innerText after second style change: ${input.innerText}`);
}
#input {
  border: 1px solid black;
}
<p>Enter multiple lines of text into the box below, then click the button</p>
<div id="input" contenteditable="true"></div>
<button onclick="performExperiment()">Experiment</button>
 
 
If we have a look at the innerText documentation, we see the first step of the behavior for the getter is defined as follows:
- If this is not being rendered or if the user agent is a non-CSS user agent, then return this's descendant text content.
Note: This step can produce suprising results, as when the innerText getter is invoked on an element not being rendered, its text contents are returned, but when accessed on an element that is being rendered, all of its children that are not being rendered have their text contents ignored.
So when our div is not being rendered, we should expect that innerText returns the textContent of our div. Indeed, that is what we see.
const input = document.querySelector("#input");
function performExperiment() {
  input.style.display = "none";
  console.log(`innerText: ${input.innerText}`);
  console.log(`textContent: ${input.textContent}`);
}
#input {
  border: 1px solid black;
}
<p>Enter multiple lines of text into the box below, then click the button</p>
<div id="input" contenteditable="true"></div>
<button onclick="performExperiment()">Experiment</button>
 
 
So why are the newlines present in our innerText when the div is visible? The documentation continues:
- Let results be a new empty list 
- For each child node node of this: 
- Let current be the list resulting in running the inner text collection steps with node. Each item in results will either be a string or a positive integer (a required line break count).
In this case, innerText is ignoring textContent and is instead operating on the childNodes list. Let's see what the value of that is for our div:
const input = document.querySelector("#input");
function performExperiment() {
  input.childNodes.forEach(node => {
    console.log(node.toString());
  });
}
#input {
  border: 1px solid black;
}
<p>Enter multiple lines of text into the box below, then click the button</p>
<div id="input" contenteditable="true"></div>
<button onclick="performExperiment()">Experiment</button>
 
 
As you can see, pressing the ENTER key adds a newline to the content of our div by adding a div to the childNodes list of our div. Why this is the case is outside the scope of this question, but would make for a good question on its own.
If you're working on an in-page editor, the HTML spec has a section containing best practices.
To recap:
If the div is visible, the innerText getter uses the textContent property of the div.
If the div is not visible, the inner text collection steps are followed for each node in the childNodes tree and the results are concatenated together.
When computing the value of innerText for our div, the value of the display attribute matters because it determines whether the textContent property or the evaluation of the childNodes tree will be used.
Note: There's a little more information in this answer by @Domino.